---
title: Classes
slug: sdk/ios-sdk-api-reference-classes
createdAt: 2026-11-04T11:40:22.092Z
---

## KindlyChatClient

`class`

```swift
public final class KindlyChatClient: Loggable
```

iOS SDK for connecting and interacting with the Kindly Chat API.

| Member                                                                                                                                                                                                                                                     | Declaration                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `shared`                                                                                                                                                                                                                                                   | `public static var shared: KindlyChatClient!`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | The shared singleton instance of the Kindly Chat SDK. Available after calling `start()`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `config`                                                                                                                                                                                                                                                   | `public static var config: KindlySDKConfig`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | The SDK configuration for permissions, auth callbacks, and form settings.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `connectionState`                                                                                                                                                                                                                                          | `public static var connectionState: ChatConnectionState`                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | The current connection state of the SDK.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `inputFieldHidden`                                                                                                                                                                                                                                         | `public static var inputFieldHidden: Bool`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | Whether the input field is currently hidden                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `delegate`                                                                                                                                                                                                                                                 | `public static var delegate: KindlyChatClientDelegate!`                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | A static delegate that gets or sets the delegate of the shared instance of `KindlyChatClient`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `init(config:behaviorType:)`                                                                                                                                                                                                                               | `public init(config: CoreSDKConfig, behaviorType: BehaviorTypeEnum)`                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | Initialize Kindly Chat SDK. In most cases, you will only want to have one instance of `KindlyChatClient`. Our "Getting started" guide covers this topic and gives you instructions on how to configure the SDK. **Parameters** - `config`: API keys and other configuration of type `KindlySDKConfig`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `events`                                                                                                                                                                                                                                                   | `static var events: AnyPublisher<KindlyEvent, Never>`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | Unified event publisher for all Kindly events (matches web API pattern) Subscribe to this to receive all events: load, message, buttonClick, ui, state                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `state`                                                                                                                                                                                                                                                    | `static var state: AnyPublisher<KindlyEventState, Never>`                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | Unified state publisher that emits state change events Subscribe to this to get real-time updates of: isChatDisplayed, isConnected, isStarted, isSocketConnected                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `notificationDelegate`                                                                                                                                                                                                                                     | `static var notificationDelegate: NotificationDelegate`                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | The shared notification delegate for handling Kindly push notifications. Assign this to `UNUserNotificationCenter.current().delegate` to let the SDK process incoming notifications.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `verboseLogging`                                                                                                                                                                                                                                           | `static var verboseLogging: Bool`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | The current state of the SDK.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `setCustomTheme(_:)`                                                                                                                                                                                                                                       | `static func setCustomTheme(_ theme: Theme)`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | Sets a custom theme for the chat. **Parameters** - `theme`: The theme to be applied to the chat. This theme will override any theme retrieved from Kindly's settings.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `setCustomTheme(background:botMessageBackground:botMessageText:buttonBackground:buttonOutline:buttonSelectedBackground:buttonText:defaultShadow:inputBackground:inputCursor:inputText:navBarBackground:navBarText:userMessageBackground:userMessageText:)` | `static func setCustomTheme( background: UIColor? = nil, botMessageBackground: UIColor? = nil, botMessageText: UIColor? = nil, buttonBackground: UIColor? = nil, buttonOutline: UIColor? = nil, buttonSelectedBackground: UIColor? = nil, buttonText: UIColor? = nil, defaultShadow: UIColor? = nil, inputBackground: UIColor? = nil, inputCursor: UIColor? = nil, inputText: UIColor? = nil, navBarBackground: UIColor? = nil, navBarText: UIColor? = nil, userMessageBackground: UIColor? = nil, userMessageText: UIColor? = nil )` | Sets a custom theme for the chat using the CustomTheme struct. **Parameters** - `background`: Background color for the chat interface. - `botMessageBackground`: Background color for bot message bubbles. - `botMessageText`: Text color for bot messages. - `buttonBackground`: Background color for buttons. - `buttonOutline`: Outline color for buttons. - `buttonSelectedBackground`: Background color for selected buttons. - `buttonText`: Text color for buttons. - `defaultShadow`: Default shadow color. - `inputBackground`: Background color for the input field. - `inputCursor`: Color for the input cursor. - `inputText`: Text color for the input field. - `navBarBackground`: Background color for the navigation bar. - `navBarText`: Text color for the navigation bar. - `userMessageBackground`: Background color for user message bubbles. - `userMessageText`: Text color for user messages.                                                                                                                                                             |
| `clearCustomTheme()`                                                                                                                                                                                                                                       | `static func clearCustomTheme()`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | Clears any custom theme that has been set and reverts to the API theme or default theme.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `openChatOnNotificationTap`                                                                                                                                                                                                                                | `static var openChatOnNotificationTap: Bool`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | Whether tapping a Kindly notification opens the chat. Defaults to `true`. When the tap arrives before `start()` (the notification launched the app) the SDK remembers it and opens the chat once `start()` has been called and the app is active. A tap older than a minute is dropped.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `notificationMode`                                                                                                                                                                                                                                         | `static var notificationMode: KindlyNotificationMode`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | How Kindly push notifications reach this app. Defaults to `KindlyNotificationMode.automatic`. The SDK tells the backend when it registers for push, and the backend sends a visible, decrypted-on-device alert only to an app that can decrypt it. With `.automatic` the SDK detects that by itself: it looks for a Notification Service Extension in your app that is built on the Kindly SDK. Set `.alert` or `.silent` to override. A change applies from the next token registration, which happens every time the chat connects.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `enableCrashReporting`                                                                                                                                                                                                                                     | `static var enableCrashReporting: Bool`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Whether the SDK should send crash reports via Sentry. Defaults to `true`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `start(botKey:languageCode:market:authTokenCallback:)`                                                                                                                                                                                                     | `class func start(botKey: String, languageCode: String = "en", market: String, authTokenCallback: AuthTokenCallback? = nil)`                                                                                                                                                                                                                                                                                                                                                                                                          | Start the Kindly Chat SDK. The SDK will automatically use .native behavior for optimal performance and compliance. - botKey: The bot key for the bot you want to connect to. - languageCode: The language code for the chat. Defaults to "en". - market: **Required** market slug for the "Markets" feature (different UI/greeting/responses per location). You must pass the market configured for the bot. The SDK loads that market's settings (`/settings/{botKey}/{market}.json`) and tells `/connect` which market was picked. If the market's settings can't be loaded the chat won't load — there is **no fallback** to another market and no per-bot default settings. Find your market slugs in the Kindly platform (Settings → General → Details & markets). - authTokenCallback: A callback function that will be called when the authentication token is available.                                                                                                                                                                                                  |
| `start(botKey:languageCode:market:authCredentialsCallback:)`                                                                                                                                                                                               | `class func start( botKey: String, languageCode: String = "en", market: String, authCredentialsCallback: @escaping AuthCredentialsCallback )`                                                                                                                                                                                                                                                                                                                                                                                         | Starts the SDK with full authentication credentials rather than a bare token. Use this overload when your identity provider issues more than a single JWT — a companion access token, an issuer the backend has to verify against, or an opaque token whose expiry only you know. Everything else behaves exactly like the token overload. - botKey: The bot key for the bot you want to connect to. - languageCode: The language code for the chat. Defaults to "en". - market: **Required** market slug — see the token overload. - authCredentialsCallback: Called with the current `chatId` and a `Promise` you fulfil with a `KindlyAuthCredentials`.                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `displayChat(languageCode:triggerDialogueId:)`                                                                                                                                                                                                             | `class func displayChat(languageCode: String? = nil, triggerDialogueId: String? = nil)`                                                                                                                                                                                                                                                                                                                                                                                                                                               | This function is used to display the chat interface. It first checks if there is a key window available, and if there is a root view controller. If both conditions are met, it creates a new instance of KindlyChatNavigationController. **Parameters** - `languageCode`: Optional language code to use for this chat session. If provided, it will override the language set during SDK start. - `triggerDialogueId`: Optional dialogue ID to trigger after connection.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `displayChat(languageCode:triggerDialogueId:)`                                                                                                                                                                                                             | `func displayChat(languageCode: String? = nil, triggerDialogueId: String? = nil)`                                                                                                                                                                                                                                                                                                                                                                                                                                                     | This function is used to display the chat interface. It first checks if there is a key window available, and if there is a root view controller. If both conditions are met, it creates a new instance of KindlyChatNavigationController. **Parameters** - `languageCode`: Optional language code to use for this chat session. If provided, it will override the language set during SDK start. - `triggerDialogueId`: Optional dialogue ID to trigger after connection.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `setLanguage(_:)`                                                                                                                                                                                                                                          | `class func setLanguage(_ languageCode: String) -> Promise<Void>`                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Update the language for the current SDK session. This will be used for all subsequent chat connections. **Parameters** - `languageCode`: The language code to use (e.g., "en", "lt", "sv") **Returns** - Promise that resolves when language change is complete                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `setNewContext(_:)`                                                                                                                                                                                                                                        | `class func setNewContext(_ context: [String: String])`                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Set the current context that will be set in the next message or click request **Parameters** - `context`: context to be set                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `setNewContext(_:)`                                                                                                                                                                                                                                        | `func setNewContext(_ context: [String: String])`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Set the current context that will be set in the next message or click request **Parameters** - `context`: context to be set                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `clearNewContext()`                                                                                                                                                                                                                                        | `class func clearNewContext()`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | Clear the currently set context                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `clearNewContext()`                                                                                                                                                                                                                                        | `func clearNewContext()`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | Clear the currently set context                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `sendMessage(_:newContext:)`                                                                                                                                                                                                                               | `@discardableResult class func sendMessage(_ text: String, newContext: [String: String]? = nil) -> Promise<ExternalChatMessage>`                                                                                                                                                                                                                                                                                                                                                                                                      | Send a message on behalf of the user, programmatically. Lets the host app drive the conversation as if the user typed and tapped send. The SDK renders the bubble locally with a temporary id and reconciles it with the server-assigned id when the POST response returns — do **not** also display the message yourself, and do not add it to any external chat log you maintain. Behaviour: - Trims whitespace; an empty/whitespace-only string rejects with `KindlySDKError.invalidData`. - Rejects with `KindlySDKError.chatNotConnected` if called before the chat is connected. Use `KindlySDK.state` to observe `isConnected` before calling. - Per-call `newContext` overrides any context previously staged via `setNewContext(_:)`. **Parameters** - `text`: The message text to send. Whitespace is trimmed. - `newContext`: Optional per-call context attached to this message only. **Returns** - A `Promise<ExternalChatMessage>` that resolves with the reconciled message (carrying the server-assigned id and timestamp), or rejects with the underlying error. |
| `setAPNSDeviceToken(_:)`                                                                                                                                                                                                                                   | `class func setAPNSDeviceToken(_ deviceToken: Data)`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | Sets the device token. **Parameters** - `deviceToken`: The device token to be set. It is of type Data.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `setAPNSDeviceToken(_:)`                                                                                                                                                                                                                                   | `class func setAPNSDeviceToken(_ deviceToken: String)`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | Sets the device token. **Parameters** - `deviceToken`: The device token to be set. It is of type String.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `saveAuthToken(_:)`                                                                                                                                                                                                                                        | `@discardableResult class func saveAuthToken(_ token: String) -> Bool`                                                                                                                                                                                                                                                                                                                                                                                                                                                                | Saves the authentication token in the Keychain. **Parameters** - `token`: The authentication token to be saved.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `saveAuthCredentials(_:)`                                                                                                                                                                                                                                  | `@discardableResult class func saveAuthCredentials(_ credentials: KindlyAuthCredentials) -> Bool`                                                                                                                                                                                                                                                                                                                                                                                                                                     | Saves full authentication credentials in the Keychain. The direct equivalent of `saveAuthToken(_:)` for integrations whose identity provider issues more than a single JWT. The SDK uses these until they expire or the backend rejects them, then falls back to the callback given at `start(...)` if there is one. **Parameters** - `credentials`: The credentials to store. **Returns** - `false` if `start()` has not been called yet, or the Keychain refused the write.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `authenticate()`                                                                                                                                                                                                                                           | `@discardableResult class func authenticate() -> Promise<Void>`                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | Re-authenticates the live chat, asking the host callback for fresh credentials. For the case the SDK cannot detect on its own: the user signed in, or switched account, while the chat was already open. The SDK refreshes on expiry by itself, so this is only needed when the *identity* changed rather than the token. The chat, its history and its socket are all kept — this re-binds the identity in place and does not restart anything. **Returns** - A promise that fulfils once the backend has accepted the new credentials, and rejects if the callback failed or the backend refused them.                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `deauthenticate()`                                                                                                                                                                                                                                         | `@discardableResult class func deauthenticate() -> Promise<Void>`                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Signs the user out of the chat without ending it. Unbinds the identity on the backend and forgets the stored credentials. The chat carries on anonymously with its history intact — use `endChat()` instead when the conversation itself should end. Safe to call when the chat is already anonymous; it fulfils immediately. The local credentials are cleared even when the backend call fails, so a sign-out is never left half done, but the promise still rejects so the host knows the backend did not hear about it. **Returns** - A promise that fulfils once the identity has been unbound.                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `saveAuthToken(_:)`                                                                                                                                                                                                                                        | `@discardableResult func saveAuthToken(_ token: String) -> Bool`                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | Saves the authentication token in the Keychain. **Parameters** - `token`: The authentication token to be saved.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `saveAuthCredentials(_:)`                                                                                                                                                                                                                                  | `@discardableResult func saveAuthCredentials(_ credentials: KindlyAuthCredentials) -> Bool`                                                                                                                                                                                                                                                                                                                                                                                                                                           | Saves full authentication credentials in the Keychain. Replaces whatever was stored before, including the previous access token, issuer and expiries — a partial overwrite would leave the SDK sending one credential's access token alongside another's id token.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `closeChat()`                                                                                                                                                                                                                                              | `class func closeChat()`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | This function is used to close the chat by dismissing the kindlyChatNavigationController.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `closeChat()`                                                                                                                                                                                                                                              | `func closeChat()`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | This function is used to close the chat by dismissing the kindlyChatNavigationController. Always dispatches to the main thread — the public `KindlySDK.closeChat()` is callable from any thread, and the internal `endChat()` invokes this from the `disconnect()` onComplete callback which runs on the `.promises` background queue (Promise.then's default). UIKit `dismiss(animated:)` must be called on main; calling from background triggers Main Thread Checker warnings (and rare race-conditions in older iOS).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `endChat()`                                                                                                                                                                                                                                                | `class func endChat() -> Promise<Void>`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Ends the active chat session. Messages, token and current connection will be cleared. This will call the "user left" API in Kindly Chat. **Note** Please note that the Chat UI will be dismissed after using this function.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `kill()`                                                                                                                                                                                                                                                   | `class func kill() -> Promise<Void>`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | Completely kills the SDK and clears all state. After calling this method, start() must be called again before using any other SDK functions. This is more destructive than endChat() which only disconnects the chat session.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `endChat()`                                                                                                                                                                                                                                                | `func endChat() -> Promise<Void>`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Ends the active chat session. Messages, token and current connection will be cleared. This will call the "user left" API in Kindly Chat. **Note** Please note that the Chat UI will be dismissed after using this function.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `notificationReceived(_:)`                                                                                                                                                                                                                                 | `class func notificationReceived(_ userInfo: [AnyHashable: Any])`                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Handles a received notification from the `didReceiveRemoteNotification` path (silent pushes). The SDK decrypts it, asks `KindlyChatClientDelegate.shouldHandleNotification`, and posts a local banner unless the user is on the chat screen. For a visible push delivered while the app is in the foreground use `notificationWillPresent(_:completionHandler:)` instead, which can also decide what iOS presents. **Parameters** - `userInfo`: The user information dictionary that contains data related to the notification.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `notificationReceived(_:)`                                                                                                                                                                                                                                 | `class func notificationReceived(_ notification: UNNotification)`                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Handles the received notification. **Parameters** - `notification`: The notification that was received.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `notificationWillPresent(_:completionHandler:)`                                                                                                                                                                                                            | `class func notificationWillPresent(_ notification: UNNotification, completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)`                                                                                                                                                                                                                                                                                                                                                                                        | Decides what iOS should present for a notification delivered while the app is in the foreground. Call it from your own `UNUserNotificationCenterDelegate.willPresent` and pass the completion handler through. The SDK returns banner and sound for notifications that are not Kindly's, and for Kindly notifications applies its display rule: nothing while the user is on the chat screen, the decrypted content when the Notification Service Extension already rewrote it, and its own decrypted banner in place of the placeholder when there is no extension. **Parameters** - `notification`: The notification handed to `willPresent`. - `completionHandler`: The completion handler handed to `willPresent`.                                                                                                                                                                                                                                                                                                                                                            |
| `notificationResponseReceived(_:)`                                                                                                                                                                                                                         | `class func notificationResponseReceived(_ response: UNNotificationResponse)`                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Handles the received notification response (the user tapped the notification). Opens the chat when `openChatOnNotificationTap` is on and the notification is Kindly's. **Parameters** - `response`: The notification response that was received.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `isKindlyNotification(_:)`                                                                                                                                                                                                                                 | `class func isKindlyNotification(_ userInfo: [AnyHashable: Any]) -> Bool`                                                                                                                                                                                                                                                                                                                                                                                                                                                             | Checks if the notification is from Kindly. Works before `start()`, so it can route a tap that launched the app. **Parameters** - `userInfo`: The user information dictionary from the notification. **Returns** - A boolean indicating whether the notification is from Kindly.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `triggerDialogue(id:)`                                                                                                                                                                                                                                     | `class func triggerDialogue(id: String)`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | Triggers a specific dialogue by its ID. The SDK must be connected before calling this method. **Parameters** - `id`: The dialogue ID to trigger.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `handleURL(_:)`                                                                                                                                                                                                                                            | `@discardableResult class func handleURL(_ url: URL) -> Bool`                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Handles a Kindly deep link URL. Supported format: `kindly://chat/dialogue/{id}` **Parameters** - `url`: The URL to handle. **Returns** - `true` if the URL was recognized and handled, `false` otherwise.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `_forceConnectionState(_:)`                                                                                                                                                                                                                                | `static func _forceConnectionState(_ state: ChatConnectionState)`                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Force a connection state for testing purposes. This method is only available in DEBUG builds. **Warning** This is for internal testing only and should never be used in production.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |

## KindlyNotificationServiceExtension

`class`

```swift
open class KindlyNotificationServiceExtension: UNNotificationServiceExtension
```

Base class for the host app's Notification Service Extension.

Subclass it in the extension target and nothing else is needed:

```swift
import Kindly

final class NotificationService: KindlyNotificationServiceExtension {}
```

iOS runs the extension for every push that carries `mutable-content: 1`, in every app state including when the app was killed. The extension decrypts the Kindly payload with the key the SDK stored in the shared keychain group during token registration, rewrites the title and body, and marks the content as decrypted so the SDK inside the app never shows it a second time. Pushes that are not Kindly's, and Kindly pushes that cannot be decrypted (no key yet, device not unlocked since boot), are delivered unchanged, which for a Kindly push means the generic placeholder the backend put in `aps.alert`.

Override `customize(_:payload:)` to add a category, thread identifier, attachments or a different sound. Hosts that already have their own extension for another provider keep it and call `KindlyPushDecryption.decrypt(userInfo:)` plus `KindlyPushDecryption.apply(_:to:)` themselves.

Both the app target and the extension target need the same `keychain-access-groups` entitlement so the extension can read the key; see the push guide.

| Member                              | Declaration                                                                                                                                     | Description                                                                                                                                                                              |
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `init()`                            | `override public init()`                                                                                                                        |                                                                                                                                                                                          |
| `didReceive(_:withContentHandler:)` | `override open func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void)` |                                                                                                                                                                                          |
| `serviceExtensionTimeWillExpire()`  | `override open func serviceExtensionTimeWillExpire()`                                                                                           |                                                                                                                                                                                          |
| `customize(_:payload:)`             | `open func customize(_ content: UNMutableNotificationContent, payload: KindlyPushDecryption.DecryptedPayload?)`                                 | Called after the SDK rewrote the content and before it is handed back to iOS. `payload` is `nil` when decryption failed and the placeholder is still in place. The default does nothing. |

## NotificationDelegate

`class`

```swift
public class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate
```

Handles push notification presentation and user interaction for the Kindly SDK. Set as `UNUserNotificationCenter.current().delegate` to let the SDK process Kindly notifications.

Notifications that are not Kindly's are presented with banner and sound, as before. For Kindly notifications the SDK decrypts, asks `KindlyChatClientDelegate.shouldHandleNotification`, shows nothing while the user is on the chat screen, presents content the Notification Service Extension already decrypted, and replaces a placeholder with its own decrypted banner when there is no extension.

| Member                                                         | Declaration                                                                                                                                                                                                        | Description |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- |
| `userNotificationCenter(_:willPresent:withCompletionHandler:)` | `public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)` |             |
| `userNotificationCenter(_:didReceive:withCompletionHandler:)`  | `public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void)`                               |             |

## Promise

`class`

```swift
public class Promise<T>
```

A lightweight promise implementation for asynchronous operations in the Kindly SDK.

Used by SDK methods like `endChat()`, `setLanguage()`, `kill()`, `sendMessage()`, and most `API.*` calls which all return `Promise<T>`. Chain results with `.then { }` and handle errors with `.catch { }`. Combine multiple promises with `whenAll(_:)` (defined in `PromiseAwait.swift`).

## Typical usage

```swift
API.greet()
    .then { result in /* handle success */ }
    .catch { error in /* handle failure */ }
```

## Threading

Callbacks default to `DispatchQueue.promises` (background, concurrent). If you touch UI or `@Published`, opt into main:

```swift
API.connect().then(on: .main) { /* safe for UIKit */ }
```

## Internal design quirk: outer vs inner promise

When created via the convenience init `Promise<T> { resolve, reject in … }`, this class holds both `self` AND a separate inner `Promise<T>` (referenced by `self.promise`). The work-block's `resolve`/`reject` callbacks operate on `self`. The external `.fulfill(_:)` / `.reject(_:)` methods operate on `self.promise`. For Promises created via the plain `init()`, `self.promise == self` (lazy) so both paths converge on the same state — but for work-block-initialized Promises, the two paths target different objects. **Do not mix the two patterns.** Either resolve via the work block's closure, or use `.fulfill()` externally — never both.

| Member          | Declaration                                                                                                                | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| --------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `onFulfilled`   | `public typealias onFulfilled = (T) throws -> Void`                                                                        | Closure type for success callbacks. May `throw`; thrown errors are converted to `_reject(_:)` automatically by the dispatch in `then`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `onRejected`    | `public typealias onRejected = (Error) -> Void`                                                                            | Closure type for failure callbacks. Cannot throw.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `resolve(_:)`   | `public static func resolve(_ value: T) -> Promise<T>`                                                                     | Returns a Promise already in the `.fulfilled` state with the given value. Useful for short-circuit returns in code paths that must return a `Promise<T>` but already have the value synchronously.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `reject(_:)`    | `public static func reject(_ error: Error) -> Promise<T>`                                                                  | Returns a Promise already in the `.rejected` state with the given error.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `fulfill(_:)`   | `func fulfill(_ value: T)`                                                                                                 | Resolves this Promise with the given value. Operates on `self.promise` (the inner reference) — see "Internal design quirk" in the class doc. For Promises made via the plain `init()`, `self.promise == self`. For Promises made via the work-block init `Promise(on:_:)`, `self.promise` is a separate object — calling `.fulfill()` on those will resolve the inner, not the outer that was returned to the caller. Don't mix patterns.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `reject(_:)`    | `func reject(_ error: Error)`                                                                                              | Rejects this Promise with the given error. Same caveat as `.fulfill(_:)` above.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `then(on:_:)`   | `@discardableResult func then(on queue: DispatchQueue = .promises, _ onFulfilled: @escaping onFulfilled) -> Promise<T>`    | Registers a success callback. The closure is dispatched to `queue` (default `.promises`, a background concurrent queue) when (or once) the Promise is fulfilled. Thread-safe: `lock` ensures the `isClosed` check + `callbacks.append` are atomic w\.r.t. `_resolve`/`_reject`. Callback execution itself happens via `queue.async`, so user code never runs while holding `lock`. If the Promise is already cancelled (or gets cancelled later), `onFulfilled` will not fire — the inner `state == .fulfilled` check gates it. **Parameters** - `queue`: Queue on which to invoke `onFulfilled`. Default is `.promises` (background). For UI work, pass `.main`. - `onFulfilled`: Called with the resolved value. May `throw` — thrown errors trigger `_reject(_:)` on this Promise. **Returns** - `self` for chaining.                                                                                                                       |
| `catch(on:_:)`  | ``@discardableResult func `catch`(on queue: DispatchQueue = .promises, _ onRejected: @escaping onRejected) -> Promise<T>`` | Registers a failure callback. Dispatched to `queue` (default `.promises`) when the Promise is rejected. Same thread-safety guarantees as `then(on:_:)`. Cancelled Promises do NOT fire `.catch` (cancellation is distinct from rejection). **Parameters** - `queue`: Queue on which to invoke `onRejected`. Default `.promises` (background). - `onRejected`: Called with the rejection error. **Returns** - `self` for chaining.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `always(on:_:)` | `@discardableResult func always(on queue: DispatchQueue = .promises, _ block: @escaping () -> Void) -> Promise<T>`         | Registers a callback that fires regardless of fulfillment or rejection — like a `finally` block. Dispatched to `queue` (default `.promises`) once the Promise resolves either way. **Note** Does NOT fire on cancellation — `.always` runs only when the Promise actually resolves (fulfilled or rejected). Cancelled Promises drain their callbacks list without firing them. If you need a true "fires-no-matter-what" hook including cancellation, you'll need to track cancellation separately. **Parameters** - `queue`: Queue on which to invoke `block`. Default `.promises` (background). - `block`: Called with no arguments. Cannot inspect value/error here. **Returns** - `self` for chaining.                                                                                                                                                                                                                                     |
| `cancel()`      | `func cancel()`                                                                                                            | Cancel this Promise. Pending callbacks (`.then`, `.catch`, `.always` already attached) will not fire. Callbacks attached AFTER cancellation also will not fire (their inner state check gates them). **No-op if the Promise has already resolved (fulfilled/rejected) or already been cancelled.** Cancellation only takes effect on a still-pending Promise. **Does NOT abort underlying work.** This Promise impl has no concept of "abort the network request that's filling this Promise". If your network call later calls `.fulfill()` / `.reject()` on a cancelled Promise, those calls silently no-op (existing `state == .pending` guard) — the result just gets dropped. The HTTP/socket request continues to run on the wire. **`await()` caveat**: a Promise cancelled while `await()` is suspended will cause `await()` to block forever (semaphore is never signalled). Don't mix `cancel()` with `await()` on the same Promise. |
| `isCancelled`   | `var isCancelled: Bool`                                                                                                    | `true` if this Promise has been explicitly cancelled via `.cancel()`. False for pending, fulfilled, and rejected Promises.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `pipe(to:)`     | `func pipe(to target: Promise<T>)`                                                                                         | Forwards this Promise's resolution to `target`. When `self` fulfills, `target` is fulfilled with the same value; when `self` rejects, `target` is rejected with the same error. Used internally by `connectSDK()` and similar to expose an internal Promise's result through a wrapper.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |

## ThemeManager

`class`

```swift
public final class ThemeManager
```

A central class to manage Theme support.

| Member             | Declaration                                 | Description                                                                              |
| ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `shared`           | `public static let shared = ThemeManager()` |                                                                                          |
| `currentTheme`     | `public var currentTheme: Theme`            |                                                                                          |
| `setUserTheme(_:)` | `public func setUserTheme(_ theme: Theme)`  | Sets a custom user-defined theme. **Parameters** - `theme`: The custom theme to be used. |
| `clearUserTheme()` | `public func clearUserTheme()`              | Clears the user-defined theme and reverts to API theme or default theme.                 |
| `hasUserTheme()`   | `public func hasUserTheme() -> Bool`        | Returns true if a user-defined theme has been set via setCustomTheme().                  |

## TypingEventManager

`class`

```swift
public final class TypingEventManager
```

Manager for typing events

| Member                              | Declaration                                                                | Description                                       |
| ----------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------- |
| `shared`                            | `public static let shared = TypingEventManager()`                          |                                                   |
| `typingEventSubject`                | `public let typingEventSubject = PassthroughSubject<TypingEvent, Never>()` | Publisher for typing events that UI can listen to |
| `emitTypingEvent(_:)`               | `public func emitTypingEvent(_ event: TypingEvent)`                        | Emit a typing event                               |
| `emitBotStartedTyping(duration:)`   | `public func emitBotStartedTyping(duration: TimeInterval?)`                | Convenience method to emit bot started typing     |
| `emitBotStoppedTyping()`            | `public func emitBotStoppedTyping()`                                       | Convenience method to emit bot stopped typing     |
| `emitAgentStartedTyping(duration:)` | `public func emitAgentStartedTyping(duration: TimeInterval?)`              | Convenience method to emit agent started typing   |
| `emitAgentStoppedTyping()`          | `public func emitAgentStoppedTyping()`                                     | Convenience method to emit agent stopped typing   |
