Skip to main content
The iOS SDK shows Clarity’s support chat as a native bottom sheet or full-screen view. It pre-warms the chat in the background so it opens instantly, reports new replies for your own unread badge, and never crashes your app: every failure is reported to you instead. Requirements: iOS 15 or later, Swift 5.10 or later (Xcode 15.3 or later). Works in UIKit and SwiftUI apps. No third-party dependencies.

Get your credentials

In the cabinet, go to Settings > Mobile SDK, click Add Integration, choose iOS and enter your app’s bundle identifier as the App ID. The integration dialog shows: The channel’s App ID must equal your bundle identifier; the SDK sends it when the chat session starts and a mismatch is refused. These values are identifiers, not secrets, so shipping them in the app is fine.

Install

  1. In Xcode, choose File > Add Package Dependencies…
  2. Paste https://github.com/Anecdote-AI/clarity-ios-sdk.
  3. Set the dependency rule to Up to Next Major Version, from 1.3.0.
  4. Add the ClaritySDK product to your app target.

Initialize

Call Clarity.initialize once at launch:
In a UIKit app, do the same in application(_:didFinishLaunchingWithOptions:).

Configuration options

Identify the user

After your user logs in, and before the first open:
ClarityUser also takes phone and avatar.
  • Calling identify with a different externalId is a logout plus login: the previous user’s session is wiped, so conversations never leak between users.
  • Calling it again with the same externalId and new profile fields updates the profile and keeps the conversation.
  • On logout, call Clarity.resetSession().

Verify the user (signed identity)

ClarityUser.jwt is not in ClaritySDK 1.3.0. It ships in the next release; this section describes it so you can prepare your backend.
An externalId alone is a claim the app makes about itself. To have Clarity trust it, your backend signs a short-lived JWT for the user with the channel’s identity secret, and the app passes it as jwt:
The secret is in the iOS integration’s dialog under Identity verification. The token format, the claims and the Require a verified identity switch are the same as for the web widget: see Verify the visitor.
  • When the token expires, fetch a new one and call identify again with the same externalId. The conversation is kept.
  • A refused token is reported through onError as .pageLoadFailed with code 401, and the message names the reason.
Never put the identity secret in the app. Anyone who extracts it from the binary can impersonate any of your users. ClarityUser.userHash is deprecated and was never verified.

Open the chat

The chat appears only once it has loaded and painted. If it cannot load, the failure arrives through your listener instead of a blank sheet. In an app with several scenes, Clarity.show(mode:sheetHeightFraction:from:) presents from a view controller you choose.

Badge your Help button

Every ClarityListener method is optional:
You can also read Clarity.unreadCount, Clarity.isWidgetVisible and Clarity.currentTicketStatus directly. unreadCount is a flag: 1 while unseen replies exist, else 0. Delivery while the chat is closed is best effort. When freshness matters, for example when the app returns to the foreground, call Clarity.refresh(). Missed replies and status changes arrive through the same listener.

Attributes and product events

These calls are safe from any thread at any time; values queue until the chat session is live. Product events are processed once product events are enabled for your organization; ask your Clarity contact.
If your workspace marks contact attributes as required, set them right after Clarity.initialize, before the chat first loads. A session missing a required attribute is refused, and show() reports .pageLoadFailed naming the missing keys. With autoPreload on, the first load starts about 1.5 seconds after launch; alternatively set autoPreload: false and call Clarity.preload() once the attributes are set.

Notifications

While the app is running

When a reply arrives with the chat closed, the SDK raises onNewMessage, sets the unread flag and posts a local notification, but only if your app already has notification permission. The SDK never prompts. Route taps back to the chat from your UNUserNotificationCenterDelegate:
To show banners while your app is in the foreground, your willPresent delegate must also allow them, for example completionHandler([.banner, .sound]).

Push notifications (APNs)

Forward your device token so replies can reach the device while the app is in the background:
setDeviceToken also accepts the token as a hex string.
Background push delivery is enabled per organization. Ask your Clarity contact about availability.

Attachments, camera and microphone

Customers can attach images and PDFs. Photo Library and Choose File need no permissions. Take Photo uses the camera inside your app, and iOS terminates an app that opens the camera without a usage description. Add one to enable it:
If your organization uses voice calling or dictation in the chat, add a microphone usage description too:
Without these keys the SDK disables the camera option and hides the microphone button rather than letting iOS close your app.

Customization

The chat’s colours, logos, font and texts come from Settings > Widget in the cabinet, the same settings as the web widget. Per-app switches such as voice calling, dictation and the Inbox tab are in the iOS integration’s dialog. In code you control the presentation (presentationMode, sheetHeightFraction), language and autoFocusInput.

API reference

Troubleshooting

Every failure arrives in onError as a ClarityError with a kind:
Check onError. .handshakeRejected means the organization id, integration key or App ID is wrong; the App ID must equal your bundle identifier. .network means the device cannot reach baseUrl. In debug builds, a diagnostic screen names the reason.
Local notifications need your app to have asked for notification permission. The SDK never prompts.
Add NSCameraUsageDescription to your Info.plist.
Clarity.previewWidgetUrl(...) returns it, and onDebugEvent on your listener traces each step: the session handshake, navigation and the chat becoming ready.