In-App Messaging (API Pull) — iOS Overview
Paylisher can deliver in-app messages to your iOS app in two independent ways:
| Delivery mode | Transport | When the message reaches the device |
|---|---|---|
| APNs push | A silent data push through Apple / Firebase | Server-initiated; can reach the device even in the background. |
| API pull (this section) | The SDK polls Engage over HTTPS | The SDK asks "is there anything for me?" and Engage answers with the currently-matching campaigns. No APNs dependency. |
This section is about the API-pull mode — in-app messages that do not depend on push infrastructure, that appear while the user is actively using the app, and that you can target to specific screens. The behavior is identical to the Android track — same config, same triggers, same render pipeline.
The pipeline
┌──────────────────────────── Engage ────────────────────────────┐
App is foreground │ resolves your project/source from the public sdkKey │
or a screen changes │ → matches LIVE, API-pull in-app campaigns for this person │
│ │ → applies audience / screen target / time window / de-dup │
▼ │ │
SDK POST ──────────┼──▶ {host}/v1/push/inapp/fetch (X-SDK-Key header) │
SDK ◀──────────────┼── { "messages": [ { "payload": { … } } ] } │
│ └─────────────────────────────────────────────────────────────────┘
▼
shouldDisplay? (time window) → de-dup (1h per pushId) → queue
▼
render (banner / modal / fullscreen / carousel / native) — skips excluded screens
▼
POST {host}/v1/push/inapp/ack status = DELIVERED → later SEEN / CLICKED / DISMISSED
The SDK sends only a public sdkKey (by default your SDK apiKey). Engage reverse-resolves the team / project / source server-side, so the app never embeds those IDs.
The request
POST {host}/v1/push/inapp/fetch
{
"distinctId": "<current distinct id>",
"sdkKey": "<public sdk key>",
"platform": "ios",
"maxMessages": 1,
"target": "wallet"
}
Header: X-SDK-Key: <public sdk key>. A successful response is 201 with a messages array (possibly empty). Acknowledgements go to the sibling path …/inapp/ack.
The default endpoint is derived from your SDK host as {host}/v1/push/inapp/fetch. In most deployments Engage lives on a different host and path prefix than analytics ingestion (for example https://api-eu.paylisher.com/engage/v1/push/inapp/fetch). In that case set fetchEndpoint explicitly — see Integration.
When does the SDK fetch? (default behavior)
With the default config (autoFetchOnForeground = true), the SDK fetches automatically on:
| Trigger | Behavior |
|---|---|
| App becomes active | ~2s after didBecomeActive |
| The user changes screen inside the app | on viewDidAppear (debounced 15s) |
| Manual | PaylisherSDK.shared.refreshEngageInAppMessages(target:) |
The screen-change fetch lets a campaign published while the user is browsing appear after one navigation, without a background→foreground cycle. Both automatic triggers send target = nil; the manual call lets you pass the current screen — see Screen targeting.
The screen-change fetch fires on viewDidAppear. In a pure SwiftUI app that stays under one host controller, navigation pushes and the initial appearance fire it, but plain TabView switches may not. For reliable per-screen behavior use a manual per-screen refreshEngageInAppMessages(target:) — see Scenarios.
What happens to a returned message
- Time window —
condition.displayTime(60s tolerance) andcondition.expireDategate whether it shows now. - De-duplication — the same
pushIdis not shown twice within a 1-hour window; a campaign already delivered to adistinctIdis not returned again. - Queue & render — rendered on the main thread. If the current screen is in
excludedActivities(default["Splash"]), rendering is deferred to the next non-excluded screen. - Delay — render is delayed by
max(condition.delay minutes, displayTime − now). - Acknowledge —
DELIVEREDon enqueue;SEEN/CLICKED/DISMISSEDfollow interaction.
Next steps
- Integration — enable the feature and set the config.
- Scenarios & Recommended Configs — pick the config for your use case.
- Campaign Setup (Dashboard) — author a pull campaign.
- Troubleshooting — empty fetch,
401, or nothing renders.