Internal handoff · iOS

App launch setup for beginners

This document explains how the app starts: Splash → ATT → Adjust → OneSignal tags → In-App Message → Home. Follow it step by step. You do not need deep SDK experience.

1. Who does what

Area Owner
Splash screen UI, ATT prompt, Adjust SDK, fetching IDs, sending tags, waiting for OneSignal user, firing app_ready, navigating to Home You (app developer)
OneSignal dashboard: In-App Message HTML, Full Screen layout, trigger app_ready = true, offer URL / iframe Already done by the OneSignal owner — do not recreate or change it
Your goal Make the iOS app initialize SDKs correctly, send the right tags, fire the trigger, and only then move from Splash to Home — while the In-App Message can stay on top. OneSignal App ID and Adjust token are provided by the human in the implementation prompt (not guessed).

2. What we want the user to see

  1. App opens → Splash image fills the screen.
  2. iOS may ask tracking permission (ATT).
  3. While still on splash, an In-App Message (IAM) appears (offer page).
  4. After the IAM is visible (or after a timeout), the app switches to Home underneath the IAM (no animation flash).
  5. The IAM stays on top. Home is ready behind it.

The offer is shown by OneSignal IAM (not a custom in-app browser screen).

3. Simple words (glossary)

TermMeaning
Splash The first full-screen image while the app prepares.
ATT App Tracking Transparency — Apple’s “Allow tracking?” popup (iOS 14+).
Adjust Attribution SDK. Gives us adid, and helps with idfa / idfv / campaign.
OneSignal Used here for In-App Messages only (not push notifications in this flow).
IAM In-App Message — a full-screen web message shown by OneSignal over the app.
Tag A key/value saved on the OneSignal user (example: idfa → device ID).
Trigger A condition that tells OneSignal “show this IAM now”. We use app_ready = true.
onesignalId OneSignal’s user id. We wait for it (with timeout) before relying on tags/IAM.

4. The launch flow (order of work)

Work in parallel where possible. Do not wait one-after-another if steps can overlap.

1

App launch (AppDelegate)

Initialize OneSignal immediately. Keep IAM paused so it does not show too early.

2

Show Splash (RootView)

Start the launch coordinator. Show splash image.

3

Start two things together

(A) Wait for OneSignal user id (max ~12 seconds)
(B) Show ATT → then init Adjust → fetch adid, idfa, idfv in parallel
Also listen for Adjust attribution and send campaign when it arrives (do not block for it).

4

Send tags to OneSignal

After Adjust IDs are ready and OneSignal user wait finished (or timed out), call addTags with mmp_id, idfa, idfv.

5

Unpause IAM + set trigger

Set trigger app_ready = true and unpause In-App Messages. Wait until IAM did display (or timeout ~20s).

6

Go to Home

After IAM is shown (+ short hold) or timeout: switch to Home with animations disabled. IAM can stay on top.

Important Wall-clock time should be roughly max(ATT+Adjust IDs, OneSignal user) plus IAM display time — not the sum of every step one after another.

5. Dependencies, frameworks & Info.plist

OneSignal (SPM / packages to link)

Add the OneSignal iOS SDK (5.x) and link these products to your app target:

Product / frameworkRequired?Why
OneSignalFramework Yes Core OneSignal SDK (user, tags, init).
OneSignalInAppMessages Yes In-App Messages (IAM). Without this, IAM will not show.
OneSignalLocation No Not needed for this flow.
OneSignalExtension No Notification Service Extension only — do not use for this IAM-only flow.
Do not ask for push permission You do not need push notification capabilities or the Notification Service Extension for this flow.

Adjust (SPM + system frameworks)

Link AdjustSdk (not Adjust WebBridge). Also add these system frameworks to the app target (usually Optional / Weak):

FrameworkRequired?Why
AdjustSdk (SPM) Yes Attribution, adid, campaign, ID helpers.
AdSupport.framework Yes IDFA access.
AppTrackingTransparency.framework Yes ATT permission prompt (iOS 14+).
AdServices.framework Yes (recommended) Apple Search Ads / AdServices attribution support.
StoreKit.framework Yes (recommended) SKAdNetwork APIs.
WebKit.framework Yes (recommended) Used with Adjust / web surfaces; also needed for OneSignal IAM WebViews.
AdjustWebBridge No Not used in this flow — do not link it.

In Xcode: select the app target → GeneralFrameworks, Libraries, and Embedded Content (and/or Package Products).

Info.plist keys you must have

KeyWhy
NSUserTrackingUsageDescription Required by Apple for the ATT popup text.
OneSignal_in_app_message_hide_gray_overlay = true Cleaner full-screen IAM (less gray chrome).
OneSignal_in_app_message_hide_drop_shadow = true Cleaner full-screen IAM (no drop shadow).
NSAdvertisingAttributionReportEndpoint Send SKAdNetwork postback copies to Adjust: https://adjust-skadnetwork.com
AttributionCopyEndpoint AdAttributionKit postback copies to Adjust: https://adjust-skadnetwork.com
SKAdNetworkItems Network IDs for Meta / Google / TikTok / Snapchat — see next section.

6. SKAdNetwork (Meta, Google, TikTok, Snapchat)

Add these IDs under SKAdNetworkItems in Info.plist so Apple can attribute installs when you advertise on those networks (with Adjust). All values must be lowercase.

NetworkSKAdNetworkIdentifier
Meta (Facebook)v9wttpbfk9.skadnetwork
Meta (Instagram)n38lu8286q.skadnetwork
Googlecstr6suwn9.skadnetwork
TikTok22mmun2rn5.skadnetwork
TikTok / Pangle238da6jt44.skadnetwork
TikTok / Panglegta9lk7p23.skadnetwork
Snapchat424m5254lk.skadnetwork
Snapchat8s468mfl3y.skadnetwork
<key>SKAdNetworkItems</key>
<array>
  <dict><key>SKAdNetworkIdentifier</key><string>v9wttpbfk9.skadnetwork</string></dict>
  <dict><key>SKAdNetworkIdentifier</key><string>n38lu8286q.skadnetwork</string></dict>
  <dict><key>SKAdNetworkIdentifier</key><string>cstr6suwn9.skadnetwork</string></dict>
  <dict><key>SKAdNetworkIdentifier</key><string>22mmun2rn5.skadnetwork</string></dict>
  <dict><key>SKAdNetworkIdentifier</key><string>238da6jt44.skadnetwork</string></dict>
  <dict><key>SKAdNetworkIdentifier</key><string>gta9lk7p23.skadnetwork</string></dict>
  <dict><key>SKAdNetworkIdentifier</key><string>424m5254lk.skadnetwork</string></dict>
  <dict><key>SKAdNetworkIdentifier</key><string>8s468mfl3y.skadnetwork</string></dict>
</array>

<key>NSAdvertisingAttributionReportEndpoint</key>
<string>https://adjust-skadnetwork.com</string>
<key>AttributionCopyEndpoint</key>
<string>https://adjust-skadnetwork.com</string>
Also in Adjust / ad dashboards (not Xcode) Connect Meta, Google, TikTok, and Snap partners in Adjust. For Snapchat, create a Snap App ID and paste it into Adjust.

7. Files you will create / touch

Keep launch / IAM code separate from your product UI:

YourApp/
  Launch/          ← Splash, ATT, Adjust, OneSignal, IAM gate
    AppDelegate.swift
    LaunchCoordinator.swift   (SplashView + RootView)
    OneSignalManager.swift
    AdjustManager.swift
    WaitingTimeLog.swift      (optional)
  App/             ← Your real home / product screens
    HomeView.swift            (sample uses ContentView)
  YourApp.swift               (@main → RootView)
FileFolderResponsibility
AppDelegate.swift Launch/ Initialize OneSignal as early as possible (IAM paused).
LaunchCoordinator.swift Launch/ Orchestrates splash → ATT → Adjust → tags → IAM → Home.
OneSignalManager.swift Launch/ Init, tags, wait for user id, trigger, IAM lifecycle.
AdjustManager.swift Launch/ ATT, Adjust init, fetch IDs, campaign callback → tag.
WaitingTimeLog.swift (optional) Launch/ Debug timing logs. Filter console by waitingTime.
HomeView.swift (or your main UI) App/ Product home only — not part of the IAM launch flow.
YourApp.swift (@main) root Attach AppDelegate + show RootView.

In RootView, replace HomeView() / ContentView() with your real home screen. Full samples: .

8. OneSignal in the app (your job)

Use your real OneSignal App ID in code.

// Pseudo-code
OneSignal.initialize(appId, withLaunchOptions: launchOptions)
OneSignal.InAppMessages.paused = true   // IMPORTANT: do not show yet
// add lifecycle listener (onDidDisplay)
// add user observer (onesignalId)

Rules

  • Initialize OneSignal in AppDelegate (before splash logic). This lets OneSignal create the user while ATT is showing.
  • Keep IAM paused until tags are sent and you are ready to show.
  • Wait for OneSignal.User.onesignalId with a timeout (example: 12 seconds). Continue even if empty after timeout.
  • Do not ask for push permission.
Dashboard / HTML IAM Already configured by the OneSignal owner (Full Screen + app_ready). Your app only initializes the SDK, sends tags, and fires the trigger — do not recreate dashboard content.

9. ATT + Adjust (your job)

ATT

  1. Wait until the app is .active.
  2. Call ATTrackingManager.requestTrackingAuthorization().
  3. Only after the user answers → initialize Adjust.

Adjust

  1. Init Adjust with the app token (production for release builds).
  2. Fetch in parallel:
    • adid (retry a few times if empty)
    • idfa
    • idfv
  3. Implement Adjust attribution callback. When campaign is available, send OneSignal tag campaign immediately. Do not wait for campaign before continuing launch.

10. Splash and Home (your job)

  • Root UI starts on .splash.
  • Splash = full-screen image asset, ignore safe area.
  • Home = your main app UI (example: HomeView).
  • When switching splash → home, disable animations so the IAM does not flicker.
var transaction = Transaction()
transaction.disablesAnimations = true
withTransaction(transaction) {
    destination = .main
}

11. Tags you must send to OneSignal

Tag keyValueWhen
mmp_id Adjust adid After IDs fetched (and after OneSignal user wait/timeout)
idfa IDFA string Same time as above
idfv IDFV string Same time as above
campaign Adjust attribution campaign name Whenever attribution arrives (async, non-blocking)

Skip empty values (do not overwrite good tags with empty strings).

bundle_id / package name can be hardcoded in the OneSignal HTML — you do not need to tag them unless asked.

12. How the In-App Message is shown

After tags are synced:

  1. OneSignal.InAppMessages.paused = false
  2. OneSignal.InAppMessages.addTrigger("app_ready", withValue: "true")
  3. Listen for IAM lifecycle onDidDisplay.

The OneSignal dashboard message is already configured with trigger app_ready is true and Full Screen display. App code only fires the trigger.

First install note On a brand-new install, OneSignal may take several seconds (backend “425 / RYW” retries) before the IAM HTML is ready. Your timeout must allow this (example: 20 seconds). Do not assume IAM appears instantly.

13. When to open the Home screen

Open Home when the IAM gate finishes:

  • IAM onDidDisplay fired, then wait ~1 extra second, or
  • IAM wait timed out (example: 20 seconds) — still open Home so the user is not stuck on splash forever.

After navigating Home, if IAM never displayed, you may keep retrying the trigger briefly in the background.

Do not dismiss the IAM when opening Home. Home loads underneath.

14. Xcode checkpoint — do not miss these

Tick every box before you call the integration done. Missing any item below usually breaks splash, tags, IAM, or Home.

Use this as a gate Do not ship / hand off until every checkpoint is checked. Paste-ready code is in the tab.

A) Project wiring in Xcode

B) AppDelegate — OneSignal early + paused

OneSignalManager.initialize(launchOptions: launchOptions)
// inside initialize:
// OneSignal.initialize(...)
// OneSignal.InAppMessages.paused = true

C) ATT → then Adjust (order matters)

D) OneSignal tags (exact keys)

E) Wait for OneSignal user

F) Trigger + unpause (must match dashboard)

OneSignal.InAppMessages.addTrigger("app_ready", withValue: "true")
OneSignal.InAppMessages.paused = false

G) Lifecycle → Home timing

H) Placeholders replaced

Short memory list AppDelegate OneSignal paused → ATT then Adjust IDs → tags → wait onesignalId → app_ready + unpause → onDidDisplay / timeout → Home under IAM + frameworks linked + Info.plist (ATT, IAM chrome, SKAdNetwork).

15. Final checklist

16. AI agent prompt (copy & paste)

Use this when handing the doc to an AI coding agent on another iOS project. Fill in the three placeholders (doc URL, OneSignal App ID, Adjust token) and the home-screen line.

Human must paste credentials in the prompt The agent must not invent IDs. OneSignal dashboard IAM is already configured — the agent only implements the app side.
Copy the block below into the agent chat
Implement the iOS launch flow in THIS project using this documentation (read BOTH tabs — Guide and Code examples):
DOC_URL_HERE

## Credentials (use exactly — do not invent or leave placeholders)
- OneSignal App ID: PASTE_ONESIGNAL_APP_ID_HERE
- Adjust App Token: PASTE_ADJUST_APP_TOKEN_HERE

## Assumptions (do not question or rework)
- OneSignal dashboard In-App Message is ALREADY configured: Full Screen, trigger app_ready = "true".
- Do NOT create/edit OneSignal dashboard content, IAM HTML, or offer pages.
- App-side integration only.

## Non-negotiable rules
1. Code examples tab is the source of truth. Paste those Swift samples as written — do not simplify, reorder steps, merge files creatively, or invent a different architecture.
2. Create folders Launch/ (splash, ATT, Adjust, OneSignal, IAM gate) and App/ (product UI only), as in the doc.
3. Replace YOUR_ONESIGNAL_APP_ID and YOUR_ADJUST_APP_TOKEN with the credentials above.
4. Do NOT request push notification permission. Do NOT add a Notification Service Extension.
5. Initialize OneSignal in AppDelegate with InAppMessages.paused = true BEFORE splash logic.
6. Order: Splash → ATT → Adjust init → fetch adid/idfa/idfv → OneSignal tags → wait onesignalId (timeout OK) → app_ready + unpause → wait IAM onDidDisplay or timeout → Home UNDER the IAM with animations disabled.
7. Wire RootView case .main to this project's existing home screen: EXISTING_HOME_VIEW_HERE (e.g. ContentView() or MainTabView()).
8. Add/merge Info.plist keys from the doc (ATT usage text, OneSignal IAM chrome flags, Adjust SKAN endpoints, SKAdNetworkItems). Keep unrelated existing keys.
9. Link packages/frameworks from the doc: OneSignalFramework + OneSignalInAppMessages, AdjustSdk, AdSupport, AppTrackingTransparency, AdServices, StoreKit, WebKit.
10. Ensure a splash image asset exists (name splash, or update code + Assets to match).
11. When finished, walk section "Xcode checkpoint" in the Guide and reply with a checklist: done vs could-not-do (with reason).

## Done when
- Launch flow matches the doc end-to-end.
- No leftover YOUR_* placeholders.
- Checkpoint items are checked or explicitly listed as blocked.

After copying: replace DOC_URL_HERE, both credentials, and EXISTING_HOME_VIEW_HERE.

17. Out of scope (not your job)

  • Writing / pasting OneSignal IAM HTML
  • OneSignal dashboard message design (Full Screen, triggers, redisplay) — already configured
  • Landing page / offer URL content
  • Push notification setup / Notification Service Extension
  • Guessing OneSignal App ID or Adjust token — they come from the human prompt

If IAM does not appear but tags and app_ready logs are correct, escalate to the OneSignal/dashboard owner (not an app-code gap).