avias8/spacetimedb-swift
Native Swift SDK for connecting to SpacetimeDB over `v2.bsatn.spacetimedb`, decoding realtime updates, and maintaining a typed local cache.
Contents
Requirements
- Swift tools
6.2 - Apple platforms:
- macOS 15+ - iOS 17+ - visionOS: not supported yet
Package Layout
sdks/swift
├── Package.swift
├── Benchmarks/SpacetimeDBBenchmarks
├── Sources/SpacetimeDB
│ ├── Auth/KeychainTokenStore.swift
│ ├── BSATN/
│ ├── Cache/
│ ├── Network/
│ ├── Log.swift
│ ├── RuntimeTypes.swift
│ └── SpacetimeDB.swift
└── Tests/SpacetimeDBTestsAdd The SDK To A Swift Package
From GitHub releases (recommended):
dependencies: [
.package(url: "https://github.com/avias8/spacetimedb-swift.git", from: "0.21.0"),
],
targets: [
.executableTarget(
name: "MyClient",
dependencies: [
.product(name: "SpacetimeDB", package: "SpacetimeDB"),
]
),
]From a local checkout:
dependencies: [
.package(name: "SpacetimeDB", path: "../../../sdks/swift"),
],
targets: [
.executableTarget(
name: "MyClient",
dependencies: [
.product(name: "SpacetimeDB", package: "SpacetimeDB"),
]
),
]Then import in code:
import SpacetimeDBQuick Start
import Foundation
import SpacetimeDB
@MainActor
final class AppModel: SpacetimeClientDelegate {
private var client: SpacetimeClient?
func start() {
SpacetimeModule.registerTables()
let client = SpacetimeClient(
serverUrl: URL(string: "http://127.0.0.1:3000")!,
moduleName: "my-module"
)
client.delegate = self
client.connect()
self.client = client
}
func stop() {
client?.disconnect()
client = nil
}
// MARK: SpacetimeClientDelegate
func onConnect() {}
func onDisconnect(error: Error?) {}
func onIdentityReceived(identity: [UInt8], token: String) {}
func onTransactionUpdate(message: Data?) {}
func onReducerError(reducer: String, message: String, isInternal: Bool) {}
}Core API
Connect/Disconnect
let client = SpacetimeClient(serverUrl: url, moduleName: "my-module")
client.delegate = delegate
client.connect(token: optionalBearerToken)
client.disconnect()Reducers
client.send("add_person", argsData)Procedures
Typed callback:
client.sendProcedure("hello", argsData, responseType: String.self) { result in
// Result<String, Error>
}Raw callback:
client.sendProcedure("hello", argsData) { result in
// Result<Data, Error>
}async/await:
let rawData = try await client.sendProcedure("hello", argsData)
let value = try await client.sendProcedure("hello", argsData, responseType: String.self)
let timed = try await client.sendProcedure("hello", argsData, timeout: .seconds(5))
let typedTimed = try await client.sendProcedure("hello", argsData, responseType: String.self, timeout: .seconds(5))One-Off Queries
Callback:
client.oneOffQuery("SELECT * FROM person") { result in
// Result<QueryRows, Error>
}async/await:
let rows = try await client.oneOffQuery("SELECT * FROM person")
let timedRows = try await client.oneOffQuery("SELECT * FROM person", timeout: .seconds(3))Cancellation: cancel the task calling an async procedure/query API, and it throws CancellationError while removing the pending callback state.
Subscriptions
let handle = client.subscribe(
queries: ["SELECT * FROM person"],
onApplied: { /* initial snapshot applied */ },
onError: { message in /* subscription failed */ }
)
handle.unsubscribe()Client Cache
Register tables once, then read typed rows from generated caches:
SpacetimeModule.registerTables()
let people = PersonTable.cache.rowsThe SDK applies transaction updates into SpacetimeClient.clientCache and table caches are updated on the main actor.
Auth Token Persistence (Keychain)
KeychainTokenStore provides opt-in token persistence per module:
let tokenStore = KeychainTokenStore(service: "com.example.myapp.spacetimedb")
// Load on app start.
let savedToken = tokenStore.load(forModule: "my-module")
client.connect(token: savedToken)
// Save when identity/token arrives.
func onIdentityReceived(identity: [UInt8], token: String) {
tokenStore.save(token: token, forModule: "my-module")
}Network Awareness And Reconnect Behavior
SpacetimeClient supports reconnect backoff through ReconnectPolicy:
let policy = ReconnectPolicy(
maxRetries: nil,
initialDelaySeconds: 1.0,
maxDelaySeconds: 30.0,
multiplier: 2.0,
jitterRatio: 0.2
)Network path changes are monitored internally. When the device is offline, reconnect attempts are deferred; when connectivity returns, reconnect is retriggered automatically.
Compression mode can be set at client construction:
let client = SpacetimeClient(
serverUrl: url,
moduleName: "my-module",
reconnectPolicy: policy,
compressionMode: .gzip // .none | .gzip | .brotli
)Distribution Hardening
The Swift package is validated in CI for reproducibility and packaging health:
swift test --package-path sdks/swiftswift package --package-path sdks/swift resolve --force-resolved-versions- demo package builds
- benchmark smoke run
Dependency versions are pinned in sdks/swift/Package.resolved to avoid accidental drift.
For public SPM/SPI distribution from this monorepo, use the mirror runbook and automation:
sdks/swift/DISTRIBUTION.mdtools/swift-package-mirror.sh
DocC and Swift Package Index
DocC bundle and tutorials live in:
sdks/swift/Sources/SpacetimeDB/SpacetimeDB.docc
DocC build command:
tools/swift-docc-smoke.shSwift Package Index builder config is in:
sdks/swift/.spi.yml
Detailed publishing runbook:
sdks/swift/PUBLISHING.mdsdks/swift/DISTRIBUTION.mdsdks/swift/SPI_SUBMISSION_CHECKLIST.md
Swift Package Index link and badge templates (replace <owner>/<repo> with mirror coordinates):
Package: https://swiftpackageindex.com/<owner>/<repo>
Swift versions badge: https://img.shields.io/endpoint?url=https://swiftpackageindex.com/api/packages/<owner>/<repo>/badge?type=swift-versions
Platforms badge: https://img.shields.io/endpoint?url=https://swiftpackageindex.com/api/packages/<owner>/<repo>/badge?type=platformsApple CI Matrix
Swift CI runs as a platform matrix in .github/workflows/swift-sdk.yml:
macOS: tests, lockfile validation, demos, benchmark smoke, DocC buildiOS simulator: cross-build ofSpacetimeDBtargetvisionOS: intentionally not targeted yet; CI asserts.visionOS(...)is absent inPackage.swift
Logging
The SDK uses os.Logger categories:
ClientCacheNetwork
Logs are visible in Console.app and device logs using subsystem com.clockworklabs.SpacetimeDB.
Benchmarks
Benchmark target: SpacetimeDBBenchmarks
Includes suites for:
- BSATN encode/decode
- protocol message encode/decode
- reducer/procedure request+response round-trip encode/decode
- cache insert/delete throughput
Run from repo root:
swift package --package-path sdks/swift benchmark --target SpacetimeDBBenchmarksList available benchmarks:
swift package --package-path sdks/swift benchmark listRun fast smoke benchmarks (used by CI):
tools/swift-benchmark-smoke.shCapture a named reproducible baseline (raw + summary + machine metadata):
tools/swift-benchmark-baseline.sh macos-arm64-14.4-swift6.2Compare two captured baselines:
cd sdks/swift
swift package benchmark baseline compare <baseline-a> <baseline-b> --target SpacetimeDBBenchmarks --no-progressValidation Matrix
From repo root:
swift test --package-path sdks/swift
swift build --package-path sdks/swift
swift build --package-path demo/simple-module/client-swift
swift build --package-path demo/ninja-game/client-swift
swift package --package-path sdks/swift resolve --force-resolved-versions
swift package --package-path sdks/swift benchmark list
swift package --package-path sdks/swift benchmark --target SpacetimeDBBenchmarks
tools/swift-benchmark-smoke.sh
tools/swift-docc-smoke.shExamples
demo/simple-module/client-swiftdemo/ninja-game/client-swift
Package Metadata
Repository: avias8/spacetimedb-swift
Stars: 1
Forks: 0
Open issues: 0
Default branch: main
Primary language: swift
README: README.md