Troubleshooting (iOS)
Work top-down. Start with the raw curl from Integration → Verifying the fetch — it tells you whether the problem is the server (auth / campaign) or the client (SDK config / device).
1. The fetch returns 401 {"message":"Invalid sdkKey"}
The key does not resolve to an active source in the environment you are hitting. Check, in order:
- Right key —
sdkKey(or the SDKapiKeyit defaults to) is the public key for a real, active in-app source. - Right environment — the source exists in the environment your
fetchEndpointtargets (e.g. an EU key againstapi-eu, not a prod key against EU). - Right URL — the URL includes the correct prefix. Most deployments need
/engagein the path (…/engage/v1/push/inapp/fetch). A404or HTML response means the prefix/host is wrong.
Until the fetch stops returning 401, nothing can render on any device.
2. The fetch returns 201 but messages is empty
Auth is fine; no campaign matched. Re-check the Campaign Setup checklist. In practice the cause is almost always one of:
| Cause | Fix |
|---|---|
| Campaign is not API-pull (delivery mode is push) | Author it with delivery mode API pull (deliveryMode: "api"). |
| Campaign is not LIVE | Set status to LIVE. |
| Campaign is action/trigger-based | Pull only supports Everyone / Audience. |
| Audience campaign, user not a member | Test with Everyone first; ensure the user is identify()-ed and lives in the right environment. |
condition.target is set but the fetch sent a different / no target | Match the screen name, or clear the target. |
| Time window closed | displayTime in the past, expireDate in the future. |
| Wrong project / environment | The sdkKey resolves to a different project than the one that owns the campaign — see Environment matching. |
| Already delivered to this user | Each campaign is delivered to a distinctId once. Test with a fresh distinctId (or reinstall). |
| Just published — list cache | The active-campaign list can be cached briefly; wait up to a few minutes. |
3. The fetch returns a message but nothing shows
| Symptom | Cause / fix |
|---|---|
| Nothing renders, banner is blank | The campaign matched but has empty layout content. Author real content. |
| It showed once and never again | 1-hour per-pushId de-dup, and permanent per-user server de-dup. Use a fresh distinctId to re-test. |
| It renders on the wrong screen | Rendering uses the foreground screen at render time; check excludedActivities and your screen-targeting call. |
Turn on debugLogging = true and watch the Xcode console for the fetch, the status code, de-dup skips, and render decisions.
4. iOS-specific gotchas
- The automatic fetch fires on foreground and on
viewDidAppear(screen change). In a pure SwiftUI app that stays under one host controller, navigation pushes and the initial appearance fire it, but plainTabViewswitches may not. Use a manual per-screenrefreshEngageInAppMessages(target:)for reliable per-screen behavior — see Screen targeting. - If
setup()runs after the app is already active (e.g. a post-login async bootstrap), the first foreground fetch may be missed until the next background→foreground cycle. Trigger a manual fetch after setup if needed.
5. Nothing happens at all (no request)
engageInAppConfigisnil→ the feature is off. Set it beforesetup().distinctIdis empty → the fetch is skipped. Ensure the SDK has an anonymous or identified id.autoFetchOnForeground = falseand you never callrefreshEngageInAppMessages(...)→ there is no trigger. Either enable auto-fetch or add a manual call.