# iOS integration

In plain English: this page is a collection of how-tos for the Amply iOS SDK beyond "just get it running." It covers session control, deeplink scheme setup, app lifecycle bridging, and the preflight pattern for asking permissions the right way. A PM can skim the section headings to know which knobs exist; an engineer copies the Swift snippets.

**Use this when** you already have the iOS SDK initialized and you need to wire up a specific flow (deeplinks, lifecycle, permissions). **Don't use this when** you haven't finished the Quickstart yet — start there.

## Before you start

* iOS SDK initialized in `AppDelegate` (see [iOS quickstart](/developer-guide/quickstart-ios.md)).
* A strong reference held to your `Amply` instance and any listener adapters on your own side, so they stay alive for the lifetime of the app.

## Managing the session manually

On iOS the SDK does not auto-track app foreground/background. You call the lifecycle methods yourself. This is different from Android, where lifecycle is automatic.

**Signatures** (from `Amply`):

```swift
func pauseSession()
func resumeSession()
func stopSession()
```

Wire them up in your `AppDelegate`:

```swift
// AppDelegate.swift
import AmplySDK

func applicationDidBecomeActive(_ application: UIApplication) {
    AppDelegate.amplySDK?.resumeSession()
}

func applicationDidEnterBackground(_ application: UIApplication) {
    AppDelegate.amplySDK?.pauseSession()
}

func applicationWillTerminate(_ application: UIApplication) {
    AppDelegate.amplySDK?.stopSession()
}
```

If you use `UISceneDelegate`, bridge the same calls from `sceneDidBecomeActive(_:)` and `sceneDidEnterBackground(_:)` instead.

If you need to distinguish "brief interruption" (incoming call, control center, app switcher preview) from "user left the app," call `pauseSession()` from `applicationWillResignActive(_:)` instead of `applicationDidEnterBackground(_:)`. The trade-off: session timing is more sensitive to transient interruptions, but you capture the moment attention leaves the screen.

### When to pause vs. stop

* `pauseSession()` — app went to background. Session can resume when the user returns.
* `resumeSession()` — app returned to foreground before the session timeout.
* `stopSession()` — force-end the current session (for example, user logs out).

## Registering your deeplink scheme

The SDK will deliver campaign deeplinks to your app, but the scheme still needs to be declared in `Info.plist` so iOS routes URLs to you.

```xml
<!-- Info.plist -->
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLName</key>
        <string>com.example.yourapp</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>yourapp</string>
        </array>
    </dict>
</array>
```

Use the same scheme when you author deeplink URLs in the Amply dashboard (for example `yourapp://promo/summer`).

## Receiving campaign deeplinks

Register a listener after SDK init. The callback returns a `Bool` — `true` if you handled the URL, `false` to let the system default handler try.

**Signature:**

```swift
func registerDeepLinkListener(listener: DeepLinkListener)
// DeepLinkListener.onDeepLink(url: String, info: [String: Any]) -> Bool
```

```swift
// DeepLinkAdapter.swift
import AmplySDK

final class DeepLinkAdapter: NSObject, DeepLinkListener {
    func onDeepLink(url: String, info: [String: Any]) -> Bool {
        guard let parsed = URL(string: url) else { return false }
        if parsed.scheme == "yourapp", parsed.host == "promo" {
            let promoId = parsed.lastPathComponent
            Router.shared.showPromo(id: promoId)
            return true
        }
        return false
    }
}

// AppDelegate.swift
private let deepLinkAdapter = DeepLinkAdapter()
// Keep adapter retained on the AppDelegate, not as a local.
amplySDK?.registerDeepLinkListener(listener: deepLinkAdapter)
```

{% hint style="warning" %}
Keep a strong reference to the adapter on your side (e.g., an `AppDelegate` property). If your adapter deallocates, no code is left to handle incoming deeplinks.
{% endhint %}

## Universal Links

Amply deeplinks are delivered through the SDK's listener regardless of scheme format. If you also want to handle Universal Links (`https://yourapp.com/...`) tapped from outside a campaign, use the standard iOS entry point:

```swift
// AppDelegate.swift
func application(
    _ application: UIApplication,
    continue userActivity: NSUserActivity,
    restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
          let url = userActivity.webpageURL else { return false }
    Router.shared.handle(url: url)
    return true
}
```

Track the hit through Amply if it matters for targeting:

```swift
AppDelegate.amplySDK?.track(
    event: "universal_link_opened",
    properties: ["url": url.absoluteString]
)
```

## Push notification preflight popup

Asking iOS for push permission directly means a single "Allow / Don't Allow" dialog with no second chance. The common pattern is to show a soft-preflight popup via an Amply campaign first, then only call `UNUserNotificationCenter` for users who tap "Sure."

High-level flow:

1. Track an event at a good moment, for example `onboarding_completed`.
2. In the Amply dashboard, target a popup campaign at that event.
3. The popup's primary button opens a deeplink like `yourapp://permissions/push`.
4. Your deeplink handler calls `requestAuthorization`.

```swift
// DeepLinkAdapter.swift (extended)
func onDeepLink(url: String, info: [String: Any]) -> Bool {
    guard let parsed = URL(string: url) else { return false }
    if parsed.host == "permissions", parsed.lastPathComponent == "push" {
        UNUserNotificationCenter.current().requestAuthorization(
            options: [.alert, .badge, .sound]
        ) { granted, _ in
            AppDelegate.amplySDK?.track(
                event: granted ? "push_permission_granted" : "push_permission_denied"
            )
        }
        return true
    }
    return false
}
```

For the full recipe including copy tips and retry cadence, see [Soft push permission](/recipes/soft-push-permission.md).

## Listening to SDK system events

Useful during development to see when config loads, when sessions start, and which campaigns evaluate.

**Signature:**

```swift
func setSystemEventsListener(listener: SystemEventsListener)
// SystemEventsListener.onEvent(event: EventInterface)
```

```swift
final class SystemEventsAdapter: NSObject, SystemEventsListener {
    func onEvent(event: EventInterface) {
        print("[Amply] \(event.name) \(event.properties)")
    }
}

private let systemEventsAdapter = SystemEventsAdapter()
amplySDK?.setSystemEventsListener(listener: systemEventsAdapter)
```

Hold the adapter on the `AppDelegate` or another long-lived owner.

## Log level

```swift
amplySDK?.setLogLevel(level: "debug")   // 'none' | 'error' | 'warn' | 'info' | 'debug'
```

Leave it at `warn` or lower in production builds.

## ATT and IDFA

The SDK reads IDFA when App Tracking Transparency has been granted. Prompting for ATT is your app's responsibility:

```swift
import AppTrackingTransparency

ATTrackingManager.requestTrackingAuthorization { _ in
    // SDK will pick up IDFA on the next dataset read.
}
```

Request ATT on a meaningful screen, not immediately on launch — Apple rejects apps that prompt before context is established.

## Related

* [iOS quickstart](/developer-guide/quickstart-ios.md) — initial setup, not covered here.
* [Tracking events](/developer-guide/tracking-events.md) — payload shapes and common patterns.
* [Handling deeplinks](/developer-guide/handling-deeplinks.md) — cross-platform deeplink routing patterns.
* [Soft push permission](/recipes/soft-push-permission.md) — full preflight recipe.
* [SDK reference: iOS](/reference/sdk-ios.md) — full method list.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.amply.tools/developer-guide/ios-integration.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
