Skip to content

[Bug]: LocalNotifications not auto-registered on iOS (Capacitor 8.4.2 SPM) — "LocalNotifications.then() is not implemented on ios" #2588

Description

@trina-bot

Priority

  • High
  • Medium
  • Low

Plugin(s) affected

@capacitor/local-notifications

Current problem behavior

Every JS call into LocalNotifications (e.g. LN.requestPermissions(), LN.checkPermissions(), LN.schedule()) rejects on iOS with:

Unhandled Promise Rejection: Error: "LocalNotifications.then()" is not implemented on ios

The .then() variant of the "not implemented" error (rather than the expected LN.requestPermissions() is not implemented) suggests Capacitor's proxy is falling back to the web implementation, which for LocalNotifications uses a chunked async import(...).then(e => new e.LocalNotificationsWeb). Something in that async chain routes back through the native bridge and fails.

No iOS permission dialog ever appears. Notifications never fire.

Expected behavior

LN.requestPermissions() shows the iOS permission dialog, resolves to a permission state, and LN.schedule() queues notifications with iOS.

Steps to reproduce

  1. Capacitor 8.4.2 iOS app with SPM setup (npx cap add ios --packagemanager SPM)
  2. npm install @capacitor/local-notifications@8.2.1
  3. npx cap sync ios
  4. Add NSUserNotificationsUsageDescription to Info.plist
  5. Call LocalNotifications.requestPermissions() from JS
  6. Observe the "not implemented" error in the console; no permission dialog appears

What we've verified (rules out common causes)

  • Plugin class IS in the compiled binarystrings ios/App/App/Build/.../App.debug.dylib | grep LocalNotificationsPlugin returns matches. Dead-code stripping ruled out.
  • capacitor.config.json packageClassList contains "LocalNotificationsPlugin" — autogenerated by cap sync, unmodified.
  • JS bundle correctly registers the plugin proxyregisterPlugin("LocalNotifications", { web: () => import(...).then(...) }) appears in the built ESM chunk.
  • 6 other Capacitor npm plugins in the SAME setup auto-register fine and work: @capacitor/preferences, @capacitor/browser, @capacitor/camera, @capgo/capacitor-social-login, @ebarooni/capacitor-calendar, @aparajita/capacitor-biometric-auth. Same auto-discovery mechanism, same Info.plist workflow, same everything. Only @capacitor/local-notifications fails.

What we tried that didn't help

  • Skipping checkPermissions() and calling requestPermissions() directly (in case of an iOS 26 checkPermissions hang) — same "not implemented" result.
  • Force-linking plugin imports in AppDelegate.swift — no effect (class was already in the binary).
  • Custom MyViewController: CAPBridgeViewController subclass with bridge?.registerPluginInstance(LocalNotificationsPlugin()) in capacitorDidLoadcapacitorDidLoad was verified never called (NSLog missing from Xcode Debug Area) even with @objc(MyViewController) on the class and customClass="MyViewController" customModule="App" customModuleProvider="target" in the storyboard.
  • Uninstall + reinstall + Clean Build Folder — no change.

Environment

  • @capacitor/core: 8.4.2
  • @capacitor/local-notifications: 8.2.1
  • @capacitor/cli: 8.4.2
  • @capacitor/ios: 8.4.2
  • iOS deployment target: 17.0
  • Xcode: 26.6
  • Device: iPhone 17 Pro Max, iOS 26.6

Ask

Is there a reason @capacitor/local-notifications's auto-discovery via packageClassList + NSClassFromString("LocalNotificationsPlugin") fails when the class is present in the binary, and the other Capacitor npm plugins in the same setup succeed? Any known workaround for Capacitor 8.4.2 SPM users?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions