iOS
Native Swift chat SDK with a SwiftUI chat screen: AI chatbot, live-agent handoff, attachments and typing indicators. iOS 14+, Swift 5.9.
Installlink
Swift PM (note the lowercase package name):
.package(url: "https://github.com/talktothelaw/new-instance-livechat.git", from: "0.3.0")
// target dependency:
.product(name: "LiveAndAiChat", package: "liveandaichat-ios")CocoaPods: pod 'LiveAndAiChat', '~> 0.3.0'
Quick startlink
import LiveAndAiChat
let chat = try LiveAndAiChat.Builder()
.config(try LiveAndAiChatConfig(apiKey: "sk_live_abc123"))
.user(ChatUser(customerName: "Ada", customerId: "usr_123", customerEmail: "ada@example.com"))
.build()
chat.present(from: viewController)UIKit hosts call present(from:), which presents the screen and wires the built-in document picker. SwiftUI hosts embed ChatScreen(sdk:onClose:onPickFile:) directly. Unlike Android, openChat() presents no UI on iOS: it flips state and starts initialize(); the host presents the screen.
Configurationlink
| Field | Default | Notes |
|---|---|---|
apiKey | required | Publishable widget key ID; a blank key throws, a keyId:secret value warns and strips the secret |
baseUrl | https://service.newinstance.cloud | Release builds reject overrides |
gqlPath / ssePath / wsPath | /service / /graphql/stream / /graphql/ws | Transport paths |
transport / initialMessage | inherited / - | Overrides |
allowRemoteAttachmentUrls | false | Opt-in: lets attach(_:) download and upload http(s) sources |
theme | dashboard theme | ChatThemeOverride(...) local theme, ranked below the dashboard theme and above the built-in one. See Theming |
The config initialiser throws, and ChatUser requires a non-empty customerName.
Authenticated customerslink
ChatUser.customerToken carries a chat identity token minted by your backend. When set it is the authoritative identity - the server derives name, email and customer id from its signed claims - so nothing else needs to be supplied:
let chat = try LiveAndAiChat.Builder()
.config(try LiveAndAiChatConfig(apiKey: "sk_live_abc123"))
.user(ChatUser.fromToken(tokenFromYourBackend))
.build()customerId without a token is recorded for agent context only and is not treated as identity. Refreshing a token for the same customer does not look like a user switch: the SDK compares the token's subject, not the token string. See Authentication and identity.
API surfacelink
initialize, openChat, closeChat (now also dismisses the presented chat screen), sendMessage, setUser(_:) (update identity after init), attachFile(data:name:mimeType:previewUri:) (returns the queue-item id), attach(_:previewUri:), removeAttachment (cancels an in-flight upload), clearAttachments, updateDraft(_:), retryMessage, requestHandoff(reason:), sendTypingStart / sendTypingStop, destroy, addDelegate / removeDelegate; LiveAndAiChat.current() returns the most recently built instance. present(from:) now presents over whatever is topmost instead of requiring a modal-free host.
State: @Published orgConfig, connectionState and lifecycle, plus derived flowState, messages, conversation, assignment, agentTyping, unreadCount, widgetOpen, draftText. Delegate callbacks (all optional): didReceiveMessage, didSendMessage, agentTypingDidChange, connectionStateDidChange, didEncounterError, unreadCountDidChange, attachmentDidUpdate, chatDidClose. lifecycle == .failed means call initialize() again.
Errorslink
didEncounterError(_:) is the channel for "why is my chat not working". Nothing on it reaches the chat interface, and it never carries the API key, the identity token, or customer data.
LiveAndAiChatError carries type (.network, .validation, .auth, .system), message, recoverable, and code (the raw GraphQL or transport code). Call sdkCode(usingIdentityToken:) to normalise onto the ChatErrorCode vocabulary shared with every other chat SDK:
func didEncounterError(_ error: LiveAndAiChatError) {
switch error.sdkCode(usingIdentityToken: true) {
case ChatErrorCode.invalidIdentityToken: refreshChatToken()
case ChatErrorCode.invalidPublicKey: reportMisconfiguration()
default: break
}
}Pass usingIdentityToken: true when the SDK was configured with a token: an auth failure cannot otherwise tell a rejected key from a rejected token. The full code list is in Authentication and identity -> Diagnosing problems.
Programmatic attachmentslink
attach(AttachmentRequest(source:name:mimeType:declaredSize:metadata:)) accepts .base64, .dataUri, .filePath (security-scoped picker URLs handled), .bytes, or .remoteUrl (config-gated); AttachmentSource.detect(_:) safely classifies strings. Validation runs before anything is queued: base64 must decode, files must be readable, non-empty and at most 25 MB, MIME must be png/jpeg/webp/gif/pdf, a wrong declaredSize is rejected as corruption, and the extension must agree with the MIME type. Uploads run off the main thread; progress and results stream through the attachment queue and attachmentDidUpdate (statuses queued, uploading, uploaded, failed, cancelled). A message is never sent with an attachment that has not finished uploading, and attachment contents are never logged.
The chat close eventlink
chatDidClose fires exactly once per chat presentation: the in-chat X (closeButton), a dismissal gesture (gesture), closeChat() (programmatic), destroy() (sessionEnded), or the host dismissing the screen (hostNavigation). The event carries reason, initiator (user/host/sdk), timestamp, conversationId, assignmentId, channel, previousState, unreadCount, hasDraft, pendingAttachmentCount and SDK metadata; never message contents, credentials or attachment data.
Platform noteslink
- No Info.plist usage keys are required: attachments use the system document picker. Add a Photos usage description only if your app saves chat images.
- UI strings are inline English; there is no localisation override today, and no push support.
destroy()makes the instance unusable (a later call trips a precondition); build a new instance to start again.theme,ChatUser.customerTokenandsdkCode(usingIdentityToken:)require 0.3.0.