The ChatBotKit Swift SDK is the official Swift library for the ChatBotKit API. Use it to add streaming AI chat to iOS, macOS, tvOS, watchOS and visionOS apps, or to call the API from Swift on a server or on Linux. It is built on URLSession with async/await and has no third-party dependencies.
Key Features
- Native concurrency: every call is
async, streams areAsyncThrowingStreams, and cancelling aTaskcancels the request; the client isSendableand checked by the Swift 6 compiler, so one instance can be shared across tasks - Streaming chat: receive the bot reply token by token
- Generated types:
CodableandSendablerequest and response types generated from the API specification - Session tokens: apps talk to ChatBotKit with a short-lived token and never hold the API secret
- Typed errors and retries:
FetchErrorwith the API error code, retries with backoff on 429 and 5xx responses - Linux support: works with
FoundationNetworking, so the same code runs in server-side Swift
Requirements
- Swift 6.0 or later (Xcode 16 or later)
- iOS 15, macOS 12, tvOS 15, watchOS 8, visionOS 1, or Linux
Installation
Add the package with Swift Package Manager:
dependencies: [ .package(url: "https://github.com/chatbotkit/swift-sdk", from: "0.1.0"), ], targets: [ .target(name: "MyApp", dependencies: [ .product(name: "ChatBotKit", package: "swift-sdk"), ]), ]swift
In Xcode, choose File > Add Package Dependencies and enter the same URL.
Quick Start
import ChatBotKit let client = ChatBotKit(token: "your-api-secret") let conversation = try await client.conversation.create( ConversationCreateRequest(backstory: "You are a helpful assistant.", model: "gpt-4o") ) let reply = try await client.conversation.complete( conversation.id, ConversationMessageCompleteRequest(text: "Hello!") ).value print(reply.text)swift
Streaming
Calls that can stream return a ResponsePromise. Await .value for the JSON response, or iterate .stream() to receive the events as they arrive:
let stream = client.conversation.complete( conversation.id, ConversationMessageCompleteRequest(text: "Tell me a story") ).stream() for try await item in stream { switch item.type { case .token: print(item.data.token ?? "", terminator: "") case .result: print("\nDone") default: break } }swift
Stateless completions, where you send the full history and nothing is stored, work the same way:
let stream = client.conversation.complete( ConversationCompleteRequest( messages: [ConversationCompleteRequestMessage(text: "Hi!", type: .user)], model: "gpt-4o" ) ).stream()swift
Breaking out of the loop or cancelling the surrounding task cancels the request. To handle event types that the typed stream item does not describe yet, stream them as StreamEvent, which keeps the raw type and data:
for try await event in client.conversation.receive(conversationId).stream(as: StreamEvent.self) { print(event.type, event.data) }swift
List calls stream too, which is the simplest way to walk through every item:
for try await item in client.bot.list().stream() { print(item.data.id) }swift
Session Tokens for Mobile Apps
Never ship your API secret inside an app - it can be extracted from the binary. Keep the secret on your server and give the app a short-lived session token for a single conversation.
On your server, with any ChatBotKit SDK:
let server = ChatBotKit(token: ProcessInfo.processInfo.environment["CHATBOTKIT_API_SECRET"]!) // a new conversation with a bot let session = try await server.bot.session.create("bot-id") // or an existing conversation let existing = try await server.conversation.session.create( conversationId, ConversationSessionCreateRequest(durationInSeconds: 3600) )swift
Return the conversation ID, token and expiresAt to the app, which then uses only the token:
let client = ChatBotKit(token: sessionToken) for try await item in client.conversation.complete(conversationId, ConversationMessageCompleteRequest(text: "Hi")).stream() { // update the UI }swift
A conversation session token can complete, send and receive messages, upload attachments and vote on messages of that one conversation. Everything else, such as listing conversations or reading the message history, goes through your server. Tokens last one hour by default and at most a day, so request a new one before expiresAt.
Configuration
let client = ChatBotKit( options: ChatBotKitOptions( token: "your-api-secret", baseURL: URL(string: "https://api.chatbotkit.com")!, runAsUserId: "user-id", timezone: "Europe/London", headers: ["X-App-Version": "1.0"], timeout: 60, retries: 3, retryDelay: 0.25 ) )swift
| Option | Description |
|---|---|
token | API secret (server) or session token (app). secret is an alias. |
baseURL | API base URL, e.g. http://localhost:3000 for a self-hosted platform. |
host | Replaces the host of baseURL. |
runAsUserId | Run requests as a sub-user. |
timezone | Sent with every request as X-Timezone. |
headers | Extra headers for every request. |
timeout | Request timeout in seconds. |
retries | Retries after network errors and 429/5xx responses (default 3). |
retryDelay | Delay before the first retry in seconds; doubles each time. |
session | The URLSession to use. |
Resources
The client mirrors the structure of the Node SDK:
| Client | Operations |
|---|---|
client.conversation | list, fetch, create, update, delete, complete, dispatch, send, receive, votes |
client.conversation.message | list, fetch, create, update, delete, upvote, downvote |
client.conversation.session | create |
client.conversation.attachment | list, upload |
client.bot | list, fetch, create, update, delete, upvote, downvote |
client.bot.session | create |
client.dataset | list, fetch, create, update, delete, search |
client.dataset.record | list, export, fetch, create, update, delete |
client.dataset.file | list, attach, detach, sync |
client.skillset | list, fetch, create, update, delete |
client.skillset.ability | list, export, fetch, create, update, delete, execute |
client.file | list, fetch, create, update, delete, upload, download, sync |
client.contact | list, fetch, create, update, delete, ensure |
client.contact.conversation | list |
client.user, client.user.session | list, fetch, create, update, delete; create session |
client.platform.model | list |
Endpoints without a wrapper yet can be called through the base client with the generated types:
let blueprints: BlueprintListResponse = try await client.fetch( ClientRequest(path: "/api/v1/blueprint/list", query: BlueprintListParams(take: 10)) )swift
Uploads
let file = try await client.file.create(FileCreateRequest(name: "handbook.pdf")) try await client.file.upload(file.id, data: pdfData, name: "handbook.pdf", type: "application/pdf") try await client.conversation.attachment.upload(conversationId, data: imageData, name: "photo.jpg", type: "image/jpeg")swift
The API returns a pre-signed upload request and the SDK sends the bytes straight to storage. Attachment uploads also work with a conversation session token.
Error Handling
| Error | When |
|---|---|
FetchError | Non-2xx response, with code (e.g. NOT_FOUND), message, status, data. |
AuthorizationRequiredError | A secret or connection must be authorized first; url is where to go. |
TimeoutError | The request timed out. |
CancellationError | The task was cancelled. |
do { _ = try await client.conversation.fetch("missing") } catch let error as FetchError where error.code == "NOT_FOUND" { print("No such conversation") }swift
Values the API may extend later, such as message and event types, are open types with static members like .user and .token, so a new value from the server decodes instead of failing.
Examples
The SDK repository includes two examples:
- CLI chat (
examples/cli): creates a conversation and streams replies in the terminal. Runs on macOS and Linux withCHATBOTKIT_API_SECRET=... swift run. - SwiftUI chat app (
examples/swiftui-chat): a complete iOS and macOS app with a conversation list, streaming chat view, a stop button and file attachments. It logs in with session tokens minted by a small backend; the example README describes the three backend endpoints and includes a reference implementation with the Node SDK.