Skip to main content

Swift SDK Setup

Add the Swift port of @baleybots/core to your iOS / macOS / Swift Server project.

Private package

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 ~/.netrc or the NETRC environment variable with a PAT that has repo scope:

    machine github.com
    login YOUR_USERNAME
    password 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:

TierTypeWhen to use
Tier 1Baleybots.generate(_:) / .stream(_:)One-line completions, prototyping
Tier 2ModelClientCustom loops, parallel fan-out, your own retry policy
Tier 3BaleybotGoal-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 tools
  • HTTPTool — remote tools (REST endpoints) via the same Tool protocol
  • Baleybot.process<T>(_:returning:schema:) — typed structured output via forced tool_choice
  • BaleybotStream<T>@MainActor @Observable SwiftUI 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 JSONSchema alongside your Codable type — the Apple @GenerableJSONSchema bridge lands in v0.2.
  • No MCP server discoveryHTTPTool is 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/baleybots under swift/. The mirror repo is a derived artifact synced from monorepo main.
  • Project guide: swift/AGENTS.md (named invariants, shape rules)
  • Milestone tracker: swift/docs/PLAN.md
  • Release procedure: swift/docs/RELEASE.md