# iOS SDK Integration for Mobile In-App Messaging

The preview Engage Mobile SDK connects an iOS app to Engage Studio Mobile In-App Messaging. The app sends a trigger event with `trackImmediately`; the SDK requests an offer from Realtime Personalization, renders a supported message, and handles the WebView boundary.

Preview
The native SDK integration is in preview. APIs, package distribution, message payloads, and rendering behavior may change before general availability. Follow the release-specific SDK distribution instructions provided by Treasure AI.

## What the SDK Does

The SDK is a thin client. Realtime Personalization decides whether to return an experience; the SDK executes the returned decision.

| Responsibility | Owner |
|  --- | --- |
| Audience eligibility and delivery decisions | Realtime Personalization |
| Trigger event and app lifecycle timing | Customer app |
| Message selection from the response | SDK |
| Modal and Banner WebView rendering | SDK |
| Hosted Pages URL rendering | SDK |
| App-specific deep-link routing | Customer app |
| App-specific custom actions | Customer app |
| Campaign impression, click, and dismiss measurement | SDK |


## Before You Start

Prepare the following values:

- A write-only API key for your Treasure AI account.
- A records endpoint for the account region.
- A Personalization endpoint.
- A Personalization token.
- A database and table for the trigger events.
- An event schema that identifies the app screen or action that should trigger a message.


The SDK package declares iOS 12 or later support. Confirm the supported OS range for the preview artifact you receive before building your application.

## Install the SDK Modules

The preview package provides two modules:

- `TreasureData` — event collection, buffering, and upload.
- `TreasureDataEngage` — the optional iOS WebKit rendering layer for Mobile In-App Messaging and Hosted Pages.


The distribution method for the preview package is not yet part of the public installation instructions. Contact your Treasure AI representative for preview access and the package version to use.

## Quick Start

Import both modules and configure the SDK once during app startup.

```swift
import TreasureData
import TreasureDataEngage

TreasureData.initializeWithApiKey(
    "<WRITE_ONLY_API_KEY>",
    apiEndpoint: "https://<region>.records.in.treasuredata.com"
)

let td = TreasureData.sharedInstance()
td.defaultDatabase = "<DATABASE>"
td.defaultTable = "<EVENT_TABLE>"
td.personalizationEndpoint =
    "https://p13n-api.treasuredata.com"
td.personalizationToken = "<PERSONALIZATION_TOKEN>"

// Needed to associate measurement with an installation.
td.enableAutoAppendUniqId()

// Optional values exposed to the message as window.TDContext.
td.profileContext = [
    "member_tier": "gold"
]

// Required for rendering on iOS.
TDEngage.install()
```

Call `trackImmediately` when a screen or event that can trigger a message occurs:

```swift
td.trackImmediately([
    "event": "pageview",
    "screen_name": "home"
])
```

You can use the optional callback to determine whether the SDK displayed a message:

```swift
td.trackImmediately([
    "event": "pageview",
    "screen_name": "campaign"
], table: "<EVENT_TABLE>") { shown in
    print("In-App message shown: \(shown)")
}
```

`addEvent` and `uploadEvents` continue to send ordinary events. They do not trigger a Mobile In-App Message.

## Configure the Core APIs

| API or property | Purpose |
|  --- | --- |
| `personalizationEndpoint` | Endpoint used to request a message offer. |
| `personalizationToken` | Read credential used for Personalization. Keep it separate from the write API key. |
| `defaultDatabase` / `defaultTable` | Default destination used to build trigger requests and ordinary events. |
| `trackImmediately(_:table:onResult:)` | Sends a trigger event and requests a message. |
| `profileContext` | JSON-serializable values exposed to message content as `window.TDContext`. |
| `TDEngage.install()` | Registers the iOS rendering layer with the core SDK. |
| `linkHandler` | Lets the app claim custom schemes or other URLs. |
| `bridgeDelegate` | Receives app-specific `invoke` calls from message content. |
| `openUrl(_:context:)` | Displays a URL that the app already has, such as a URL from a push payload. |


## Message Flow

1. The app calls `trackImmediately(event)`.
2. The TreasureData iOS SDK sends the trigger event to Personalization.
3. Realtime Personalization returns an offer or no offer.
4. `TreasureDataEngage` selects one displayable message.
5. The SDK checks the one-message-at-a-time guard.
6. The SDK renders Modal, Banner, or Hosted Pages.
7. The app and message content handle the resulting interaction.


If no campaign matches, the SDK does not display anything. This is a normal result, not an error.

The SDK selects a single displayable message from the response. If another message is already visible, the new message is not displayed.

## Hosted Pages

Hosted Pages is the customer-facing name for a campaign message whose content is loaded from a URL. The SDK displays the URL in a full-screen WebView and injects the configured profile context.

Use Hosted Pages for:

- Coupon pages.
- Stamp cards.
- Lotteries and interactive promotions.
- Surveys and forms.
- Personalized campaign pages.


Hosted Pages does not use the Modal or Banner appearance settings. The hosted page is responsible for providing an appropriate way to leave the experience.

### Campaign-Delivered Hosted Pages

When the app calls `trackImmediately` and Realtime Personalization returns a Hosted Pages offer, the SDK has the campaign identity needed for campaign measurement.

### Direct URL Display

When the app already has a URL, it can call `openUrl`:

```swift
let url = URL(string: "https://example.com/coupon")!
TreasureData.sharedInstance().openUrl(
    url,
    context: ["coupon_id": "abc123"]
)
```

This is useful for the push-tap path. The URL is displayed in the SDK WebView, but it did not come from a campaign offer, so it does not produce campaign `impression`, `click`, or `dismiss` records.

## Handle Links

Use `linkHandler` when the app owns a custom URL scheme:

```swift
td.linkHandler = { url in
    guard url.scheme == "engage-demo" else {
        return false
    }

    AppRouter.shared.open(url)
    return true
}
```

Return `true` only when the app handled the URL. If the handler returns `false` or is not configured, the SDK uses its default behavior for supported web URLs. Do not open a second browser from the app and then return `false`.

The handler is called synchronously on the main thread. Keep the handler short and route longer work to the appropriate app service after claiming the URL.

## Handle Custom Actions

Message content can request app-specific behavior through the SDK bridge. The app receives the request through `TDEngageBridgeDelegate`:

```swift
final class EngageClient: NSObject, TDEngageBridgeDelegate {
    func handleTDBridgeInvoke(
        name: String,
        params: [String: Any]
    ) {
        switch name {
        case "grantPoints":
            // Check authentication, authorization, and parameter limits.
            break
        default:
            break
        }
    }
}

td.bridgeDelegate = engageClient
```

The SDK does not interpret the action name or authorize the request. Treat the message content and every parameter as untrusted input. The delegate is weak, so assign an object whose lifetime covers the app rather than a short-lived view controller.

## Share Profile Context with Message Content

`profileContext` is injected into Modal, Banner, and Hosted Pages content as `window.TDContext`.

```swift
td.profileContext = [
    "member_tier": "gold",
    "points": 1200
]
```

Follow these rules:

- Use JSON-serializable values only.
- Set the context again after each app launch; it is not persisted by the SDK.
- Clear or replace it when the signed-in customer changes.
- Do not include API keys, tokens, passwords, or values that the Hosted Pages page should not see.
- Pass large numeric identifiers as strings to avoid JavaScript precision loss.


## Handle Screen Changes

The Personalization request is asynchronous. A customer can leave the triggering screen before the response arrives. The preview SDK exposes a presentation gate and returns the in-flight task from the callback overload so the host app can suppress or cancel a stale request.

Use this when a message must only appear on the screen that triggered it:

```swift
let task = td.trackImmediately([
    "event": "pageview",
    "screen_name": "checkout"
], table: "<EVENT_TABLE>") { shown in
    // Update app-level diagnostics if needed.
}

// Cancel when the triggering screen is no longer valid.
task?.cancel()
```

The exact presentation-gate API is part of the preview contract and may change before general availability.

## Measurement

In the preview SDK flow, measurement is recorded automatically for campaign-delivered messages:

- `impression` — the experience is presented.
- `click` — a supported link or bridge action is used.
- `dismiss` — the experience is closed.


`enableAutoAppendUniqId()` is effectively required when you need to associate these records with an installation. The measurement destination and schema may change before general availability.

For troubleshooting, see [Mobile In-App Troubleshooting](/products/marketing-cloud/engage-studio/channels/mobile-inapp/troubleshooting).

## Minimal Integration Checklist

You can integrate the preview SDK without using a sample application repository.

- Add the preview `TreasureData` and `TreasureDataEngage` modules through the distribution method provided with your SDK artifact.
- Initialize the core SDK once during app startup.
- Set the Personalization endpoint, token, database, and event table.
- Call `TDEngage.install()` after core initialization.
- Configure `profileContext`, `linkHandler`, and `bridgeDelegate` only when your content needs them.
- Call `trackImmediately` when the configured screen or event occurs.
- Test Modal, Banner, and Hosted Pages on supported iOS devices.
- Register Push tokens separately if the app uses Mobile Push.
- Verify impression, click, dismiss, Deep Link, and Hosted Pages behavior before release.


The preview SDK's package name, version, and distribution URL are release-specific. Use the installation instructions that accompany the artifact rather than a source repository URL.