Usage

Configuring DixaMessenger

Configure the DixaMessenger once, when the app launches. Do it in an AppDelegate: it's the one place that runs once at launch, and you'll need it for push notifications too, because iOS delivers the push token only to an AppDelegate.

The example below works for SwiftUI and UIKit apps. In a UIKit app that already has an AppDelegate, add the Messenger.configure call to its didFinishLaunchingWithOptions. A SwiftUI app adds the AppDelegate with @UIApplicationDelegateAdaptor and leaves out @main.

import DixaMessenger
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        // Creates a new DixaConfiguration and configures the DixaMessenger client
        let config = DixaConfiguration()
            .logLevel(.all)
            .apikey("<YOUR_DIXA_MESSENGER_TOKEN>")
        #if DEBUG
            // Builds run from Xcode get push notifications from the APNs sandbox - the default is .production
            .pushEnvironment(.sandbox)
        #endif

        Messenger.configure(config)

        return true
    }
}

Configuration options

DixaConfiguration is a value type: each modifier returns a new configuration.

ModifierWhat it setsDefault
apikey(_:)Your Messenger token. Required–
logLevel(_:)How much the SDK logs to the console. See Log level.none
pushEnvironment(_:)Which APNs environment push notifications come from. See Push environment.production
supportedLanguages(_:)Conversation languages, as two-letter codes. Overrides the languages set in the Dixa Dashboard. The UI language always follows the device settingsDashboard settings

Pass the configuration to Messenger.configure(_:). You can also create it in one line with DixaConfiguration(token:logLevel:pushEnvironment:).

Example:

// One line configuration
DixaConfiguration(token: "<YOUR_DIXA_MESSENGER_TOKEN>", logLevel: .error, pushEnvironment: .sandbox)

// Note that the Configuration is a value type, and a new one is returned when calling a convenience modifier
DixaConfiguration()
    .apikey("<YOUR_DIXA_MESSENGER_TOKEN>")
    .logLevel(.error)
    .pushEnvironment(.sandbox) // default .production
    .supportedLanguages(["en", "da"])

Log level

LevelWhat's logged
.noneNothing
.errorCritical events only (for example, an invalid Messenger token)
.warningWarnings and critical events (for example, a message that failed to send)
.allEvery valuable event (for example, a message sent), including warnings and errors

Set it with logLevel(_:) on the configuration you pass to Messenger.configure(_:).

Push environment

BuildPush environment
Run from Xcode (debug).sandbox
TestFlight and App Store.production (the default)

Set .sandbox in debug builds only, as in the AppDelegate example above.

Authentication

You can decide to have your users identified. The SDK supports three authentication modes:

ModeHow the user is identified
Anonymous (default)Not identified. No user information is shared with the agent
ClaimedA username and email supplied by the host app, or entered by the user in a form
VerifiedA JWE token encrypted with a private key from the Dixa Dashboard

In the Dixa Dashboard, under Settings > Messenger > (your messenger) > Authentication, Select a method sets how users must be identified before they can start a chat:

Method in the dashboardIf your app hasn't identified the user
Allow anonymous usersEveryone can chat
Allow users to claim their identityThe SDK shows a form that asks for a name and email
Programmatically authenticate the usersThe user can't start a chat

⚠️ Call order matters: Messenger.configure(_:) must be called before Messenger.updateUserCredentials(username:email:), Messenger.verifyUser(with:) and the clear methods. Configure at app launch, as shown above, so it has always run by the time the user logs in. See Switching users or logging out for the full lifecycle.

Claimed

Authentication is done using a username and email that the host app provides, or, if missing, that the SDK prompts the user to enter via a form.

MethodWhat it does
Messenger.updateUserCredentials(username:email:)Stores the user's name and email. Safe to call again with the same values. To identify a different user, call clearUserCredentials() first. The email isn't validated
await Messenger.clearUserCredentials()Removes the stored name and email. Call it on logout, or before signing in a different user on the same device

Example: setting claimed credentials after the user signs in to the host app. Messenger.configure(_:) already ran at app launch.

Messenger.updateUserCredentials(username: username, email: email)

Verified

Authentication is done using a JWE token, encrypted with a private key from the Dixa Dashboard under Settings > Messenger > (your messenger) > Authentication. The private key is a shared secret, not one half of a key pair: your backend encrypts the token with it and Dixa decrypts it with the same key. Your backend creates the token: see Generating a JWE token. Your app sets it in code; the SDK never asks the user for it.

MethodWhat it does
Messenger.verifyUser(with:)Stores the user's JWE token
Messenger.clearVerificationToken()Removes the stored token. Call it on logout, or before signing in a different user on the same device

Switching users or logging out

On devices shared by multiple end users — for example, a household device, a kiosk, or a user who signs out and a different user signs in — the SDK retains the previously stored credentials until they are explicitly cleared. Starting a new session without clearing them will attribute the new user's conversations to the previous user.

To correctly hand off to a new user, always clear the previous identity before setting the new one:

  1. On logout, clear both the stored credentials and the verification token. Each clear method removes only its own kind of identity.
  2. When the next user signs in, identify them. You don't need to call configure(_:) again: clearing already starts a new session.

clearUserCredentials() is async, so call it with await, and set the next user's identity only after it returns. Otherwise the SDK still has the previous user's email and ignores the new one.

// On logout from your host app (from any context: the Task runs on the main actor)
Task { @MainActor in
    await Messenger.clearUserCredentials()
    Messenger.clearVerificationToken()
}

// When the next user signs in (on the main actor, for example in your login screen)
Messenger.updateUserCredentials(username: newUsername, email: newEmail)

If your app switches users in one step, do both in the same Task, after the await.

⚠️ Important Note: Clearing credentials also wipes the local conversation history tied to that user. This is intentional — it prevents a new user from reading the previous user's conversations.

💡 A good rule of thumb: treat Messenger.configure(_:) as an app-lifecycle call (run it once in AppDelegate), and treat updateUserCredentials / verifyUser / clearUserCredentials / clearVerificationToken as user-session-lifecycle calls (run them in response to login and logout in your host app).

Launching the Messenger

The Dixa Messenger works with both SwiftUI and UIKit. Add a button to your app that opens it.

UI frameworkCallPresentation
SwiftUIMessenger.openMessenger()Returns a SwiftUI view: use it as the content of .fullScreenCover or .sheet
UIKitMessenger.openMessenger(from:with:completion:)Presents the Messenger from your view controller with the UIModalPresentationStyle you pass
UIKitMessenger.openMessenger(from:completion:)Presents the Messenger full screen

The UIKit completion is called when the presentation finishes.

Example (SwiftUI apps):

import SwiftUI
import DixaMessenger

struct YourHostingView: View {

    // MARK: - States
    @State var openMessenger: Bool = false

    // MARK: - View
    var body: some View {
        VStack {
            Button {
                openMessenger.toggle()
            } label: {
                Text("Contact Support")
                    .foregroundStyle(.black)
            }
        }
        .fullScreenCover(isPresented: $openMessenger) {

            /// MARK: - Present it
            Messenger.openMessenger()
        }
    }
}

#Preview {
    YourHostingView()
}

Example (UIKit apps):

@objc func contactSupportTapped() {
    Messenger.openMessenger(from: self, with: .pageSheet)
}

Change the widget size

The Messenger opens full screen by default. You can present it as a modal instead.

In SwiftUI, choose the modifier: a sheet, a full screen cover, or a sheet with a specific height (all presentation detents).

.sheet(isPresented: $presentMessenger) {
  Messenger.openMessenger()
}

.fullScreenCover(isPresented: $presentMessenger) {
  Messenger.openMessenger()
}

.sheet(isPresented: $presentMessenger) {
  Messenger.openMessenger()
     .presentationDetents([.medium, .large])
}

In UIKit, pass the style to openMessenger(from:with:completion:) (all modal presentation styles).

Unread messages

⚠️ Deprecated: this API is deprecated.

The SDK offers an option for the hosting application to show how many unread messages there are. The SDK keeps one listener: each call replaces the previous one.

Messenger.unreadMessagesCountListener(completion: @escaping (_ count: Int) -> Void)

Push notification handling

DixaMessenger doesn't listen for notifications by itself. Your app sets up push notifications with Apple and in the Dixa Dashboard, then forwards the push token and notifications to the SDK.

Setting up the push notification certificate

  1. Get an Apple Push Notification service (APNs) certificate.

  2. Enable the Push Notifications capability for your bundle identifier (App ID), in the Apple Developer portal or in Xcode if Xcode manages provisioning for you.

  3. Export the certificate in .p12 format. For example, import it into Keychain Access, then export it as .p12.

  4. Log in to the Dixa Dashboard and choose the organization you're setting up push notifications for.

  5. Go to Settings, then Messenger under Channels and flows. Find your messenger in the list and select the settings (cog) icon on its right. This opens the page titled Messenger > { Messenger name }.

    Select messenger

  6. Select the Setup tab, then Push notifications.

  7. Select Add platform and choose the platform:

    PlatformUse it for
    Apple Push Notification serviceTestFlight and App Store builds
    Apple Push Notification service (Sandbox)Builds run from Xcode with .pushEnvironment(.sandbox)

    Setup push certificate

  8. Select Choose file and pick your .p12 file. Enter its password if it has one, then select Load credentials from file. The dashboard fills in Certificate and Private key from the file.

  9. Select Submit platform.

Setting up push notifications in the application

In the app, ask the user for permission to show notifications, register for remote notifications, and forward the push token to the DixaMessenger. It's safe to forward the token before Messenger.configure(_:) has run: the SDK keeps it.

The code below goes in the AppDelegate from Configuring DixaMessenger, so it's the same for SwiftUI and UIKit apps.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // ... Messenger.configure(config), as in Configuring DixaMessenger

    // Receive willPresent and didReceive (set it before launch finishes, so taps that launch the app arrive too)
    UNUserNotificationCenter.current().delegate = self
    // Without permission, iOS delivers the notification but doesn't show it
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { _, _ in }
    // Ask APNs for the device token: it arrives in didRegisterForRemoteNotificationsWithDeviceToken
    application.registerForRemoteNotifications()
    return true
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    Messenger.pushNotification.register(deviceToken: deviceToken)
}

In your UNUserNotificationCenterDelegate, let DixaMessenger check whether each notification is for the Messenger. The check happens in the app.

Delegate methodSDK callWhat your app does
willPresent (app open)Messenger.pushNotification.presentNotification(_:withCompletionHandler:) returns true for a Messenger notificationDecide how to present it, and call the completion handler in both cases
didReceive (notification tapped)Messenger.pushNotification.process(notification:rootViewController:) opens the Messenger and returns truePass the view controller that's on screen (in a SwiftUI app, the one SwiftUI hosts your views in; the example finds it for you). If it returns false, handle the notification yourself

Messenger and Messenger.pushNotification are main-actor isolated (@MainActor). The example below implements the delegate in the AppDelegate, which also runs on the main actor; @preconcurrency lets it compile in Swift 6 mode too.

// MARK: - UNUserNotificationCenterDelegate
extension AppDelegate: @preconcurrency UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {

        // MARK: - Present notification while the app is open
        if Messenger.pushNotification.presentNotification(notification, withCompletionHandler: { _ in }) {
            // From the Messenger
            completionHandler([.banner, .list, .sound])
        } else {
            // Not from the Messenger: present it as your app usually does
            completionHandler([.banner, .list, .sound])
        }
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {

        // MARK: - Push tapped
        if let topViewController = topViewController(),
           Messenger.pushNotification.process(notification: response.notification, rootViewController: topViewController) {
            // The Messenger opened the conversation
        } else {
            // Not from the Messenger: handle it as your app usually does
        }
        completionHandler()
    }

    /// The view controller that's currently on screen.
    private func topViewController() -> UIViewController? {
        let window = UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .flatMap(\.windows)
            .first(where: \.isKeyWindow)
        var top = window?.rootViewController
        while let presented = top?.presentedViewController { top = presented }
        return top
    }
}

Push notification API

CallWhat it does
Messenger.pushNotification.register(deviceToken:)Registers the APNs device token with the SDK
Messenger.pushNotification.presentNotification(_:withCompletionHandler:) -> BoolTrue if a notification that arrived while the app is open is for the Messenger. Your app calls the completion handler
Messenger.pushNotification.process(notification:rootViewController:) -> BoolIf the notification is for the Messenger, opens it from the view controller you pass and returns true

Uploading photos

To let users upload photos, add the NSPhotoLibraryUsageDescription key and a description to your app's Info.plist.

On this page