Native Swift SDK for connecting to SpacetimeDB over v2.bsatn.spacetimedb, decoding realtime updates, and maintaining a typed local cache.
- Requirements
- Package Layout
- Add The SDK To A Swift Package
- Quick Start
- Core API
- Auth Token Persistence (Keychain)
- Network Awareness And Reconnect Behavior
- Distribution Hardening
- DocC and Swift Package Index
- Apple CI Matrix
- Logging
- Benchmarks
- Validation Matrix
- License
- Swift tools
6.2 - Apple platforms:
- macOS
15+ - iOS
18+ - visionOS
2+ - watchOS
11+
- macOS
spacetimedb-swift
├── Package.swift
├── Benchmarks/Package.swift
├── Sources/SpacetimeDB
│ ├── Auth/KeychainTokenStore.swift
│ ├── BSATN/
│ ├── Cache/
│ ├── Network/
│ ├── Log.swift
│ ├── RuntimeTypes.swift
│ └── SpacetimeDB.swift
└── Tests/SpacetimeDBTests
From GitHub releases (recommended):
dependencies: [
.package(url: "https://github.com/avias8/spacetimedb-swift.git", from: "0.23.0"),
],
targets: [
.executableTarget(
name: "MyClient",
dependencies: [
.product(name: "SpacetimeDB", package: "spacetimedb-swift"),
]
),
]From a local checkout:
dependencies: [
.package(path: "../spacetimedb-swift"),
],
targets: [
.executableTarget(
name: "MyClient",
dependencies: [
.product(name: "SpacetimeDB", package: "spacetimedb-swift"),
]
),
]Then import in code:
import SpacetimeDBimport 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) {}
}let client = SpacetimeClient(serverUrl: url, moduleName: "my-module")
client.delegate = delegate
client.connect(token: optionalBearerToken)
client.disconnect()client.send("add_person", argsData)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))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.
let handle = client.subscribe(
queries: ["SELECT * FROM person"],
onApplied: { /* initial snapshot applied */ },
onError: { message in /* subscription failed */ }
)
handle.unsubscribe()Register tables once, then read typed rows from generated caches:
SpacetimeModule.registerTables()
let people = PersonTable.cache.rowsThe SDK applies transaction updates into SpacetimeClient.clientCache. Internal cache mutation remains off the main actor; observable rows snapshots are coalesced and published on the main actor for SwiftUI safety.
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")
}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
)The Swift package is validated in CI for reproducibility and packaging health:
swift testswift test --sanitize=threadswift build -c release- iOS, visionOS, and watchOS simulator cross-builds
- an isolated benchmark package that does not add dependencies to SDK consumers
DocC bundle and tutorials live in:
Sources/SpacetimeDB/SpacetimeDB.docc
DocC build command:
xcodebuild docbuild \
-scheme SpacetimeDB \
-destination 'generic/platform=macOS' \
CODE_SIGNING_ALLOWED=NOSwift Package Index builder config is in:
.spi.yml
Detailed publishing runbook:
PUBLISHING.mdDISTRIBUTION.mdSPI_SUBMISSION_CHECKLIST.md
Package page and badge endpoints:
Package: https://swiftpackageindex.com/avias8/spacetimedb-swift
Swift versions badge: https://img.shields.io/endpoint?url=https://swiftpackageindex.com/api/packages/avias8/spacetimedb-swift/badge?type=swift-versions
Platforms badge: https://img.shields.io/endpoint?url=https://swiftpackageindex.com/api/packages/avias8/spacetimedb-swift/badge?type=platforms
Swift CI runs from .github/workflows/ci.yml:
macOS: build, unit tests, Thread Sanitizer, and DocCiOS simulator: cross-build ofSpacetimeDBvisionOS simulator: cross-build ofSpacetimeDBwatchOS simulator: cross-build ofSpacetimeDB
The SDK uses os.Logger categories:
ClientCacheNetwork
Logs are visible in Console.app and device logs using subsystem com.clockworklabs.SpacetimeDB.
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 Benchmarks benchmark --target SpacetimeDBBenchmarksList available benchmarks:
swift package --package-path Benchmarks benchmark listCompare two captured baselines:
swift package --package-path Benchmarks benchmark baseline compare \
<baseline-a> <baseline-b> \
--target SpacetimeDBBenchmarks \
--no-progressFrom repo root:
swift test
swift test --sanitize=thread
swift build -c release
swift package --package-path Benchmarks benchmark list
swift package --package-path Benchmarks benchmark --target SpacetimeDBBenchmarks
xcodebuild docbuild -scheme SpacetimeDB -destination 'generic/platform=macOS' CODE_SIGNING_ALLOWED=NOThis package carries the SpacetimeDB Business Source License 1.1 terms in LICENSE.txt. Review that file before production use.