Swift SDK Setup
Add the Swift port of @baleybots/core to your iOS / macOS / Swift
Server project.
The Swift SDK currently lives in a private mirror repo. You need
read access to baleybots/baleybots-swift on GitHub. Reach out
in the org if you don't have it yet.
Requirements
- Swift 6.2 (Xcode 26+)
- Platforms: iOS 18 · macOS 15 · watchOS 11 · tvOS 18 · visionOS 2
- Anthropic API key in
ANTHROPIC_API_KEY(or use the proxy-server mode — see Connecting to a proxy server)
The platform floor is set by Synchronization.Atomic and the
Observation framework. v0.2 (Apple Generable bridge) will bump
this to iOS / macOS 26+.
Add the package
In your Package.swift:
// swift-tools-version: 6.2
import PackageDescription
let package = Package(
name: "YourApp",
platforms: [.iOS(.v18), .macOS(.v15)],
dependencies: [
.package(
url: "https://github.com/baleybots/baleybots-swift.git",
from: "0.1.0"
)
],
targets: [
.executableTarget(
name: "YourApp",
dependencies: [
.product(name: "Baleybots", package: "baleybots-swift")
]
)
]
)
Authenticating SwiftPM to a private repo
SwiftPM uses the same credentials as git clone against
github.com. Pick whichever you already have set up:
-
SSH (recommended for development) — change the URL to
git@github.com:baleybots/baleybots-swift.git. Uses your existing SSH key. -
macOS Keychain — once you've cloned any private GitHub repo via Xcode or
gh auth login, the keychain entry covers SwiftPM too. -
Personal access token (CI) — set up
~/.netrcor theNETRCenvironment variable with a PAT that hasreposcope:machine github.comlogin YOUR_USERNAMEpassword ghp_xxxxxxxxxxxx
In Xcode, the easiest path is File → Add Package Dependencies… — Xcode handles auth via your signed-in account.
Hello, world
Once the package resolves:
import Baleybots
import Foundation
@main
struct App {
static func main() async throws {
// Tier 1 — one-liner, model auto-resolved from ANTHROPIC_API_KEY.
let answer = try await Baleybots.generate("Capital of France?")
print(answer)
}
}
Set ANTHROPIC_API_KEY in your scheme's environment (or, for a CLI
target, just export it before running) and hit ▶︎.
What's in the package
The SDK is organized as three composable tiers:
| Tier | Type | When to use |
|---|---|---|
| Tier 1 | Baleybots.generate(_:) / .stream(_:) | One-line completions, prototyping |
| Tier 2 | ModelClient | Custom loops, parallel fan-out, your own retry policy |
| Tier 3 | Baleybot | Goal-driven agent with tool loop, multi-turn history, typed structured output |
Plus building blocks that span all three:
Tool+TypedTool— local Swift functions as model toolsHTTPTool— remote tools (REST endpoints) via the sameToolprotocolBaleybot.process<T>(_:returning:schema:)— typed structured output via forcedtool_choiceBaleybotStream<T>—@MainActor @ObservableSwiftUI bridge: partial decoded values flow into your view as the model streams JSON
For the full mental-model walkthrough and per-tier examples, see the
README on the package repo.
Connecting to a proxy server
If you're routing through a @baleybots/proxy-server deployment
instead of calling Anthropic directly:
import Baleybots
import Foundation
let client = ModelClient(
model: .anthropic(.claudeHaiku45),
proxyURL: URL(string: "https://your-proxy.example.com")!
)
let answer = try await client.generate("Capital of France?")
No API key needed on the device — the proxy injects its own credentials.
Streaming structured output into SwiftUI
The piece Swift uniquely does well:
import SwiftUI
import Baleybots
struct Recipe: Codable, Sendable {
let name: String
let prepTimeMinutes: Int
let ingredients: [String]
static let schema: JSONSchema = .object([
"name": .string(),
"prepTimeMinutes": .integer(),
"ingredients": .array(of: .string()),
], required: ["name", "prepTimeMinutes", "ingredients"])
}
struct RecipeView: View {
@State private var stream = BaleybotStream<Recipe>()
let bot: Baleybot
var body: some View {
VStack(alignment: .leading) {
Button("Get recipe") {
stream.run(
"Give me a guacamole recipe",
on: bot,
returning: Recipe.self,
schema: Recipe.schema
)
}
// Updates field-by-field as the model streams JSON.
if let r = stream.partial {
Text(r.name).font(.title)
ForEach(r.ingredients, id: \.self) { Text("• \($0)") }
}
if stream.isStreaming { ProgressView() }
}
}
}
Codable + @Observable + AsyncSequence give you end-to-end
type safety the JS/TS surface doesn't have an equivalent for.
What's not in v0.1
See the deferred-questions table on the
baleybots-swift README
and docs/PLAN.md for the full list. The highlights:
- Anthropic only. No OpenAI / Google / xAI / Ollama provider yet.
- Hand-written
JSONSchemaalongside yourCodabletype — the Apple@Generable↔JSONSchemabridge lands in v0.2. - No MCP server discovery —
HTTPToolis the building block; high-level MCP integration lands later. - Text only — no image / audio / video input yet.
- No opinionated SwiftUI chat view — build it from
BaleybotStream<T>and the underlying event streams.
Source + contributing
- Swift SDK source: github.com/baleybots/baleybots-swift
- Development happens in the monorepo: PRs go to
baleybots/baleybotsunderswift/. The mirror repo is a derived artifact synced from monorepomain. - Project guide:
swift/AGENTS.md(named invariants, shape rules) - Milestone tracker:
swift/docs/PLAN.md - Release procedure:
swift/docs/RELEASE.md