Skip to content

NSE/push-sync drags in dioxus iOS lifecycle (UIApplicationMain) — illegal in app extensions #113

Description

@pvg13

Summary

When building a Notification Service Extension (NSE) that uses WaveSyncDB's
push-sync FFI, the extension staticlib pulls in WaveSyncDB's dioxus iOS
lifecycle
code, which references UIApplicationMain and UIApplication
notifications. These symbols are illegal in an app extension (built with
-application-extension / APPLICATION_EXTENSION_API_ONLY), so the extension
either fails to link or is rejected by App Review.

There is currently no way to enable push-sync (the NSE FFI) without also
dragging in the dioxus UI lifecycle module.

Environment

  • WaveSyncDB consumed with features ["derive", "dioxus", "web", "push-sync"]
    (the push-sync feature is what exposes wavesync_nse_handle_push).
  • Target: aarch64-apple-ios, linked into an NSE appex via swiftc -application-extension -e _NSExtensionMain -force_load libnse.a.
  • Downstream: Mediterranea's packages/nse crate (depends on wavesyncdb +
    db only, deliberately excludes the ui/dioxus-router crate to stay under
    the NSE memory budget — yet the UIKit symbols still arrive via wavesyncdb's
    own dioxus feature).

Symptom

swiftc link of the appex fails with:

Undefined symbols for architecture arm64:
  "_UIApplicationMain", referenced from:
      objc2_ui_kit::...::UIApplication::main::... in libmediterranea_nse.a
  "_UIApplicationDidBecomeActiveNotification", referenced from:
      wavesyncdb::dioxus::lifecycle::ios::start_lifecycle_listener::... in libmediterranea_nse.a
  "_UIApplicationWillResignActiveNotification", referenced from:
      wavesyncdb::dioxus::lifecycle::ios::start_lifecycle_listener::... in libmediterranea_nse.a
  "_kSCDynamicStoreUseSessionKeys" / "_kSCNetworkInterfaceType*", referenced from:
      system_configuration::... in libmediterranea_nse.a   (if_watch / SystemConfiguration)
ld: symbol(s) not found for architecture arm64

The wavesyncdb::dioxus::lifecycle::ios::start_lifecycle_listener reference is
the blocker: it uses UIApplication/UIApplicationMain, which are unavailable
to app extensions regardless of which frameworks are linked. (The
SystemConfiguration symbols — from libp2p's if_watch — are separately
fixable by linking -framework SystemConfiguration, and are noted only for
completeness.)

Root cause

The dioxus feature compiles wavesyncdb::dioxus::lifecycle::ios, which calls
into UIApplication. When a downstream extension enables push-sync, Cargo
feature unification (or a direct dependency edge) also brings in the dioxus
lifecycle module, so the extension-illegal UIKit code lands in the staticlib
even though the extension never runs a UIApplication.

Request

Provide an extension-safe path to the push-sync FFI that does not compile
the dioxus/UIKit iOS lifecycle. Options (any one is fine):

  1. Make push-sync fully independent of dioxus — i.e. wavesync_nse_handle_push
    and the SyncNotify registry link without pulling dioxus::lifecycle::ios.
  2. Gate the iOS lifecycle listener behind a separate, non-default feature (e.g.
    dioxus-lifecycle) so dioxus (used for the main app UI) can be enabled in
    the app target without forcing UIKit into a sibling extension target.
  3. Build the dioxus::lifecycle::ios module with #![cfg(not(app_extension))]
    or gate the UIApplication calls so the symbols aren't emitted in an
    -application-extension build.

Happy to test any of these against the Mediterranea NSE build
(swiftc -application-extension … -force_load).

Workaround in the meantime

Shipping the app without the NSE (WITHOUT_NSE=1) — the app is fully
functional; alert-class pushes just show the operator placeholder banner instead
of rewritten content, and background sync is unaffected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions