Skip to main content
Version: 5.9.0

SDK Delegate (ChatCenterUISDKDelegate)

ChatCenterUISDKDelegate is a protocol for receiving events from the SDK. All methods have default implementations (empty; didOpen returns false), so implement only the ones you need.

Thread safety

In the current SDK version all delegate methods are in practice called on the main thread, but this is not part of the contract — the protocol is not marked @MainActor. For reliability, wrap UI updates in DispatchQueue.main.async:

func chatCenterUI(chatCenter: ChatCenterUISDK, didChangeUnreadMessages count: Int) {
DispatchQueue.main.async {
self.updateBadge(count: count)
}
}

Setting the delegate

Assign the delegate after initializing the SDK:

chatCenterSDK.delegate = self

Declare conformance to the protocol in the appropriate class:

extension MyViewController: @preconcurrency ChatCenterUISDKDelegate {
// implement the methods you need
}

The @preconcurrency attribute is only needed when building with the strict concurrency checker (SWIFT_STRICT_CONCURRENCY=complete or Swift 6). Otherwise it can be omitted.


Delegate methods

didChangeUnreadMessages

func chatCenterUI(chatCenter: ChatCenterUISDK, didChangeUnreadMessages count: Int)

Called when the unread message count changes.

When it is called:

  • When the unread counter changes — after the chat history has been loaded (the SDK loads history automatically after authorize(user:); opening the chat screen is not required for this).
  • Periodically via the getUnreadMessagesCount REST request, if ChatConfig.unreadMessageCountDelay > 0 is set. The SDK sends the next request unreadMessageCountDelay seconds after the previous one completes — the actual interval may exceed the configured value by the request duration.
  • Once with a value of 0 when a new user authorizes (different identifier or token). Re-authorizing the same user with the same token does not trigger this call.

After deauthorizeUser the delegate stops receiving counter updates until the next authorization.

Parameters:

  • count: Number of unread messages. 0 means there are no unread messages.
Updating the badge when the chat is closed

While the chat screen is not open, the delegate does not receive instant updates for each incoming message — the counter is delivered via REST polling at the unreadMessageCountDelay interval. If unreadMessageCountDelay is not set or equals 0, polling does not run, and the delegate will not be called until the chat is opened again.

To update the badge when the app returns to the foreground or when a push notification arrives, additionally call getUnreadMessagesCount(completion:) on ChatCenterUISDK — the completion is delivered on the main thread:

// AppDelegate / SceneDelegate
func applicationWillEnterForeground(_ application: UIApplication) {
guard let sdk = chatCenterSDK else { return }
do {
try sdk.getUnreadMessagesCount { [weak self] result in
if case let .success(count) = result {
self?.updateChatBadge(count)
}
}
} catch {
// User is not authorized yet — refresh the counter after authorize.
}
}

Example — updating the badge on the chat button:

func chatCenterUI(chatCenter: ChatCenterUISDK, didChangeUnreadMessages count: Int) {
DispatchQueue.main.async {
self.chatButton.badge = count > 0 ? "\(count)" : nil
}
}

didReceiveNetwork

func chatCenterUI(chatCenter: ChatCenterUISDK, didReceiveNetwork error: Error)

Called on a REST request or file upload error.

REST only, not WebSocket

In the current version, this method reports only REST-layer errors (including file uploads). WebSocket connection drops and errors are not delivered through this callback — to track the real-time channel status, use separate signals (for example, the state from send(message:), which throws ChatCenterUISendMessageError.webSocketNotActive).

Example:

func chatCenterUI(chatCenter: ChatCenterUISDK, didReceiveNetwork error: Error) {
Analytics.log("chat_network_error", parameters: ["error": error.localizedDescription])
}

didOpen url

func chatCenterUI(chatCenter: ChatCenterUISDK, didOpen url: URL) -> Bool

Called before a URL from a chat message (a link in text) is opened.

Return value:

  • true — the link is handled by your application; the SDK does nothing.
  • false — the SDK tries to open the URL via UIApplication.shared.open(_:), but only if UIApplication.shared.canOpenURL(url) returned true. Otherwise the link is silently ignored.
Default behavior

If the method is not implemented, the SDK behaves as if false were returned — it tries to open the link via UIApplication.shared.open(_:) with a canOpenURL check.

LSApplicationQueriesSchemes

canOpenURL returns false for any scheme not listed in LSApplicationQueriesSchemes in your application's Info.plist. If chat messages may contain tel:, mailto:, custom deep link schemes, or third-party app links, add them explicitly:

<key>LSApplicationQueriesSchemes</key>
<array>
<string>tel</string>
<string>mailto</string>
<string>myapp</string>
</array>

Otherwise the user taps the link and sees no reaction, and the SDK does not emit an error.

Example — intercepting a deep link, external links via SFSafariViewController:

import SafariServices

func chatCenterUI(chatCenter: ChatCenterUISDK, didOpen url: URL) -> Bool {
// Handle the app's internal deep link
if url.scheme == "myapp" {
DeepLinkRouter.handle(url)
return true
}
// Open external links via Safari in the app
let safariVC = SFSafariViewController(url: url)
present(safariVC, animated: true)
return true
}
The return value is synchronous

didOpen returns Bool synchronously: the decision "I will handle it" (true) or "let the SDK open it" (false) is delivered to the SDK immediately. Asynchronous URL checks inside the method are not possible — if the decision depends on a network request, return false (subject to the canOpenURL constraints above) and handle the URL separately.


didLog

func chatCenterUI(chatCenter: ChatCenterUISDK, didLog event: String)

Called for every SDK logging event. Used to forward SDK logs into your own system — Firebase Crashlytics, Sentry, OSLog.

Depends on logLevel

The method respects ChatLoggerConfig.logLevel — events are delivered to the delegate only if their level matches the configured logLevel. With the default value .off, the delegate does not receive any events. To receive all logs in the delegate, use ChatLoggerConfig(logLevel: .all). Any additional filtering inside the delegate handler is up to your code.

Example:

func chatCenterUI(chatCenter: ChatCenterUISDK, didLog event: String) {
os_log("%{public}@", log: .default, type: .debug, event)
}

Method summary

MethodWhen it is calledReturns
didChangeUnreadMessagesWhen the unread counter changes (after history is loaded) and via REST polling at the ChatConfig.unreadMessageCountDelay interval, if it is set
didReceiveNetworkOn a REST request or file upload error (WebSocket errors are not delivered)
didOpen urlBefore opening a URL from a chat messageBool (handled?)
didLogOn an SDK logging event whose level matches ChatLoggerConfig.logLevel