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):
- Make
push-sync fully independent of dioxus — i.e. wavesync_nse_handle_push
and the SyncNotify registry link without pulling dioxus::lifecycle::ios.
- 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.
- 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.
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
UIApplicationMainandUIApplicationnotifications. These symbols are illegal in an app extension (built with
-application-extension/APPLICATION_EXTENSION_API_ONLY), so the extensioneither fails to link or is rejected by App Review.
There is currently no way to enable
push-sync(the NSE FFI) without alsodragging in the
dioxusUI lifecycle module.Environment
["derive", "dioxus", "web", "push-sync"](the
push-syncfeature is what exposeswavesync_nse_handle_push).aarch64-apple-ios, linked into an NSE appex viaswiftc -application-extension -e _NSExtensionMain -force_load libnse.a.packages/nsecrate (depends onwavesyncdb+dbonly, deliberately excludes theui/dioxus-router crate to stay underthe NSE memory budget — yet the UIKit symbols still arrive via wavesyncdb's
own
dioxusfeature).Symptom
swiftclink of the appex fails with:The
wavesyncdb::dioxus::lifecycle::ios::start_lifecycle_listenerreference isthe blocker: it uses
UIApplication/UIApplicationMain, which are unavailableto app extensions regardless of which frameworks are linked. (The
SystemConfigurationsymbols — from libp2p'sif_watch— are separatelyfixable by linking
-framework SystemConfiguration, and are noted only forcompleteness.)
Root cause
The
dioxusfeature compileswavesyncdb::dioxus::lifecycle::ios, which callsinto
UIApplication. When a downstream extension enablespush-sync, Cargofeature unification (or a direct dependency edge) also brings in the
dioxuslifecycle 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):
push-syncfully independent ofdioxus— i.e.wavesync_nse_handle_pushand the SyncNotify registry link without pulling
dioxus::lifecycle::ios.dioxus-lifecycle) sodioxus(used for the main app UI) can be enabled inthe app target without forcing UIKit into a sibling extension target.
dioxus::lifecycle::iosmodule with#![cfg(not(app_extension))]or gate the
UIApplicationcalls so the symbols aren't emitted in an-application-extensionbuild.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 fullyfunctional; alert-class pushes just show the operator placeholder banner instead
of rewritten content, and background sync is unaffected.