Troubleshooting
initialize()’s completion resolves false, or crashes in debug
If neither setApiKey() nor the OneSygnalApiKey Info.plist entry resolves to a
non-blank string, initialize() calls assertionFailure(...) before resolving the
completion with false. In a debug build, assertionFailure traps — this looks like a
crash, not a graceful failure. In a release build, assertions are compiled out, so you
just get completion?(false) (or await initialize() returning false) with no crash.
This asymmetry is a known, documented gap — Android’s equivalent path reports false in
both configurations.
The trap message is:
OneSygnal API key not found. Add key "OneSygnalApiKey" with your API key to your app's Info.plist.Fix: add the OneSygnalApiKey key to Info.plist, or call setApiKey() before
initialize().
setApiKey() doesn’t seem to do anything
Two silent-no-op cases:
- Blank string.
setApiKey("")(or all-whitespace) is a no-op — it leaves any prior override, or the plist key, in place, rather than clearing it or throwing. - Called after
initialize(). The key is read once, atinitialize()time. CallingsetApiKey()afterward has no effect until the nextinitialize()(e.g. aftershutdown()).
isInitialized() returned true, but surveys never showed and track() calls fail
initialized is set true synchronously inside initialize(), before the background
config/user/survey fetch (backgroundInit) has actually run or settled — so there’s a real
window where isInitialized() reports true while setup is still in flight. If the
fetched config’s status isn’t "active" or "trial" (a server-side kill switch),
backgroundInit flips initialized back to false and resolves the original
initialize() completion with false — but any code that already read isInitialized()
as true during that window saw a stale answer. Prefer gating on the initialize()
completion/await result or the ready event (see Events) rather
than polling isInitialized().
A call I made returned false / did nothing
track() returns false (a genuine return value, not queued) if the SDK isn’t
initialized yet, or if setSurveysEnabled(false) is in effect. identify(), logout(),
reset(), and shutdown() all no-op (their completion still fires, with false for
identify) if called before initialize() has completed — there is no call-queuing on
iOS the way the web loader queues pre-ready calls. Call these only after initialize()’s
completion (or ready) has fired.
Survey didn’t show even though a trigger should have matched
SurveyWindowOverlay.show() fails closed if there’s no foreground UIScene available at
the moment of the attempt — the throttle state (setSurveyActive) is rolled back and
nothing is shown, with no error surfaced. This can happen if the trigger fires while the
app is backgrounded or between scene transitions. There is no retry — the next trigger
match gets its own attempt.
Building/testing locally: swift test looks green but proves nothing
The whole public API (Api/OneSygnal.swift) is wrapped in #if canImport(UIKit). A plain
swift test builds for native macOS, where UIKit doesn’t exist, so the API surface — and
every test against it — silently compiles away. Use
xcodebuild test -scheme OneSygnalSDK -destination 'platform=iOS Simulator,name=<device>,OS=latest'
instead. An unavailable simulator device name makes xcodebuild exit 70 without running
anything, which reads like a build failure rather than a missing simulator — check the
device name first if that happens.
LocalOverride.swift pointed my build at the wrong host/key
Sources/OneSygnal/Api/LocalOverride.swift defines localApiHostOverride and
localApiKeyOverride, both checked ahead of the plist/production values, for pointing a
dev build at a local stack. It’s marked git update-index --skip-worktree, so a non-nil
local value won’t show up in git status — and because the Flutter bridge vendors this
SDK’s source directory wholesale, a local override here silently applies to Flutter iOS
builds too. If native calls are hitting an unexpected host or key, check this file isn’t
set to a non-nil value.