A Swift package and a Kotlin library that put your assistant inside your own app, with a chat screen that reads Arabic right to left. Source only for now.
Mobile SDKs for iOS and Android
Put your assistant inside your own app. A Swift package and a Kotlin library share one surface: send a message, stream the answer as it is written, keep the conversation, pass the identity your server proved and run the tools your app handles. Each comes with a chat screen that reads Arabic right to left.
Source only: not yet on Swift Package Manager or Maven Central
The source of both SDKs and their guides are ready, and are checked against the live API on every change. Neither has been built with Xcode or Android Studio yet: we build and test both before the first release, and the install lines below work from then on.
Requirements
iOS 17 or later, Xcode 15.4 or later, Swift 5.9 or later.
Android 7.0 (API 24) or later, Android Studio Koala or later, JDK 17, Gradle 8.7 or later.
A publishable key from Settings > Developers, ideally limited to the one assistant the app talks to.
1. A publishable key, made for apps
Anyone who unpacks an app can read what is inside it, so an app never carries a secret key. In Settings > Developers choose Publishable key (for apps): it starts with bidi_pk_ and can only chat with the assistants it names. Every other call answers 403 secret_key_required, its limits count each phone on its own, and your plan's monthly conversations still apply. Both SDKs refuse a bidi_sk_ key outright.
A Kotlin library on coroutines and Flow, with BidiChat, a Jetpack Compose chat screen whose words come in English and Arabic.
Gradle
// settings.gradle.kts, from a checkout of the source
include(":bidi")
project(":bidi").projectDir = file("../bidi/sdks/android/bidi")
// app/build.gradle.kts
dependencies {
implementation(project(":bidi"))
// once published: implementation("ai.bidii:bidi-android:0.1.0")
}
3. Show the chat
One line gives your app a full chat screen. With Arabic the whole screen mirrors, the words come from the Arabic set, and screen readers name every bubble.
val client = BidiClient(publishableKey = BuildConfig.BIDI_PUBLISHABLE_KEY, agentId = "YOUR_AGENT_ID")
BidiChat(client = client, language = "ar")
4. Or drive it yourself
The client streams the same events the website widget gets: pieces of the answer, its sources, how the turn ended, and the whole answer last. It keeps the conversation between messages and starts a new one when the old one has expired.
Swift
var answer = ""
for try await event in client.stream("متى تفتحون اليوم؟", locale: "ar") {
switch event {
case .delta(let piece): answer += piece
case .done(let response): answer = response.message?.content ?? answer
default: break
}
}
Kotlin
var answer = ""
client.stream("متى تفتحون اليوم؟", locale = "ar").collect { event ->
when (event) {
is ChatEvent.Delta -> answer += event.text
is ChatEvent.Done -> answer = event.response.message?.content ?: answer
else -> Unit
}
}
5. Tools your app runs
Declare a client tool under Settings > Actions and register a handler with the same name. When a streamed answer needs it, the SDK runs your handler and sends the result to /api/v2/agents/{agent_id}/chat/tool-results before the assistant's 10-second deadline. The assistant reads the result as data, never as instructions. Identity works as on the web: your server signs, the app passes it to identify.
Both clients throw one error type that carries the API's stable code, its message, the request id and Retry-After: switch on the code, never on the message. Codes you will meet from an app: rate_limited, conversation_busy (one message at a time), conversation_expired, conversation_not_found, agent_unavailable, identity_rejected, identity_mismatch, plan_required, invalid_key, expired_key and secret_key_required. Pass the locale ar and the messages come back in Arabic.
Swift
do {
_ = try await client.send("مرحبا")
} catch BidiError.api(_, "rate_limited", _, _, let retryAfter) {
try await Task.sleep(for: .seconds(retryAfter ?? 5))
} catch BidiError.api(_, let code, let message, let requestId, _) {
print(code, message, requestId ?? "") // quote the request id to support
}
6. Build and verify
Each SDK carries its unit tests and a sample app that reads its key from a file outside source control. Build and test them with:
Shell
cd sdks/ios && swift build && swift test
cd sdks/android && gradle build && gradle test