<!-- AI agents: this is the Markdown version of a Reception documentation page. Index: /llms.txt -->

# Native iOS integration

Connect Reception to a SwiftUI or UIKit app. The [setup prompt](/docs/get-started#setup-prompt) guides placement, user details and the design round; this page supplies its native code. For JavaScript apps, use [React Native](/docs/react-native).

## 1. Install {#install}

Add `https://github.com/reception-sh/reception-ios.git` with **Up to Next Major Version**, product **Reception**, to your app target. For XcodeGen or Tuist, edit the manifest and regenerate. Requires iOS 17+ and Xcode 26; check an older deployment target with the developer before changing it.

## 2. Configure {#configure}

Configure once on the main actor in your existing SwiftUI `App` initializer, or UIKit `application(_:didFinishLaunchingWithOptions:)`. Keep your root view and other startup work:

```swift
import Reception

// Reception support chat. Docs for changes: https://reception.sh/llms.txt
Reception.configure(appId: "app_YOUR_APP_ID")
```

## 3. Open chat {#open-chat}

Copy your app’s existing button style and call `Reception.shared.openChat()`:

```swift
import SwiftUI
import Reception

// Inside your screen; use your own button styling.
Button("Support") { Reception.shared.openChat() }
```

`Reception.shared.closeChat()` dismisses the sheet. Your app owns the Support entry and its [unread badge](#entry).

## Appearance and language {#appearance}

Immediately after configuration, set `Reception.shared.appearance.accentColor`, `onAccentColor`, `chatBackground`, `incomingBackground`, `incomingForeground` and `fontFamily` from matching app assets or theme properties. Leave unmatched options unset. Dynamic color assets follow light/dark mode; update the options when the app’s own theme setting changes.

```swift
// Use the app's existing color asset, only if it has one with this name.
Reception.shared.appearance.accentColor = Color("AccentColor")
```

Import `SwiftUI` for `Color`. For a fixed or app-selected light/dark mode, set `appearance.preferredColorScheme` to `.light` or `.dark`; `nil` follows the app. Only with an in-app language picker, set `Reception.shared.languageOverride` to its language tag at launch and whenever it changes; `nil` follows iOS. Keep the built-in texts until the design round. See [Appearance](/docs/appearance) and [Language](/docs/language).

## Entry and unread badges {#entry}

Read `Reception.shared.unreadCount` in the SwiftUI view so observation updates the badge; hide it at zero. For example, inside your existing button label:

```swift
if Reception.shared.unreadCount > 0 {
    Text("\(Reception.shared.unreadCount)")
}
```

Apply the app’s badge style and the setup prompt’s placement rules. Badge only the tab with the main entry: SwiftUI `.badge(Reception.shared.unreadCount)` or UIKit `tabBarItem.badgeValue` (nil at zero). UIKit needs re-arming observation, not a one-time count; follow [UIKit integration](/docs/uikit). More placements: [Open & close chat](/docs/entry-point) and [Unread badges](/docs/unread-messages).

## Accounts and user details {#identity}

Call these on the main actor after configuration, using values from your app:

```swift
// A name without an account: userId is nil.
Reception.shared.identify(userId: userId, name: name, email: email)
Reception.shared.setMetadata(metadata) // Complete [String: String] dictionary, at most 20 fields.
```

Use `nil` for unavailable identity fields. Send entered names even in apps without accounts. Update identity and the complete metadata dictionary whenever they change; names/emails belong in `identify`, never metadata.

When an actual signed-in session ends (including an expired restored session or account switch), call `Reception.shared.logout()` before the next identity. Never log out a guest at startup. For deletion, `try await Reception.shared.deleteData()` before discarding the session; catch `ReceptionError`, keep the session and offer retry on failure. Both clear identity, metadata and push token; supply them again for the next session.

For verified identity, use `Reception.shared.identify(token: token)` with your server’s signed token. Handle `.identityRejected(let error)` in `Reception.shared.onEvent` through the app’s token-refresh flow. Follow [Users & accounts](/docs/accounts) for server setup, refresh, account switching and deletion limits.

## Push {#push}

Wire push even if permission has not been granted, preserving the app’s existing consent flow. Never request permission here. Import `UIKit` and `UserNotifications`, then merge these methods into the app’s delegates:

```swift
// In the app delegate (SwiftUI: reuse or add @UIApplicationDelegateAdaptor):
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken token: Data) {
    Reception.shared.setPushToken(token)
}

// In the existing UNUserNotificationCenterDelegate:
nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
    if let options = await Reception.shared.notificationPresentationOptions(userInfo: notification.request.content.userInfo) {
        return options
    }
    return [.banner, .sound] // Keep the app's existing policy for its own notifications.
}

nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async {
    if await Reception.shared.handlePushNotification(response) { return }
    // Keep the app's existing tap handling for its own notifications here.
}
```

Merge these into existing callbacks and keep other notification services working. With Firebase Messaging or OneSignal, pass the raw APNs token Data, never an FCM token or OneSignal ID. Add the Push Notifications capability when signing allows. If the app sets its icon badge itself, set Reception.shared.updatesAppBadge = false at launch.

Call `await Reception.shared.registerForPushIfAuthorized()` at launch, on foreground, after your existing permission flow succeeds, and after logout or successful deletion. It registers only when already authorized and never prompts. If there is no notification delegate, assign `UNUserNotificationCenter.current().delegate = self` in the app delegate’s launch callback. Keep the delegate alive and preserve other provider callbacks. The foreground helper handles receipt itself; do not also forward the same receipt with `handlePushNotification`. Configure credentials in [Push notifications](/docs/push-notifications), then verify actual delivery on a device.

## Paywalls {#paywalls}

Use the matching Swift helper in [Paywall cards](/docs/paywalls), started after Reception and your purchase vendor. The app owns its purchase screen; present it above chat so dismissal returns to the conversation.

## Embedded chat {#embedded}

Place `ReceptionChatView()` in your screen after configuration. Your screen owns dismissal; do not also call `openChat()`. See [Embedded chat](/docs/entry-point#custom-navigation) for layout and lifecycle details.

## Events and diagnostics {#events}

Assign `Reception.shared.onEvent` for chat, message, image, review, paywall, deletion and identity-rejection events. Set `Reception.logLevel` to `.debug` while diagnosing an integration. See the [SDK reference](/docs/sdk-reference) for the complete API.

## Build and simulator checks {#simulator-checks}

Build your app’s scheme in Debug for an installed iOS simulator with normal signing. Follow the [setup prompt](/docs/get-started#setup-prompt) to run `-ReceptionSetupCheck`, then `-ReceptionPreview welcome` and `-ReceptionPreview conversation`. The previews use local example data and send nothing.
