brightdigit/mistkit
A Swift Package for Server-Side and Command-Line Access to CloudKit Web Services
Table of Contents
- Installation - Requirements - Platform Support - Quick Start
- Authentication - Error Handling - Advanced Usage - Examples
Overview
MistKit provides a modern Swift interface to CloudKit Web Services REST API, enabling cross-platform CloudKit access for server-side Swift applications, command-line tools, and platforms where the CloudKit framework isn't available.
Built with Swift concurrency (async/await) and designed for modern Swift applications, MistKit supports all three CloudKit authentication methods and provides type-safe access to CloudKit operations.
Key Features
- π Cross-Platform Support: Works on macOS, iOS, tvOS, watchOS, visionOS, and Linux
- β‘ Modern Swift: Built with Swift 6 concurrency features and structured error handling
- π Multiple Authentication Methods: API token, web authentication, and server-to-server authentication
- π‘οΈ Type-Safe: Comprehensive type safety with Swift's type system
- π OpenAPI-Based: Generated from CloudKit Web Services OpenAPI specification using swift-openapi-generator
- π Secure: Built-in security best practices and credential management
Getting Started
Installation
Add MistKit to your Package.swift:
dependencies: [
.package(url: "https://github.com/brightdigit/MistKit.git", from: "1.0.0-beta.2")
]Or add it through Xcode:
- File β Add Package Dependencies
- Enter:
https://github.com/brightdigit/MistKit.git - Select version and add to your target
Requirements
- Swift 6.1+
- Xcode 16.0+ (for iOS/macOS development)
- Linux: Ubuntu 18.04+ with Swift 6.1+
Platform Support
Minimum Platform Versions
| Platform | Minimum Version | |----------|-----------------| | macOS | 11.0+ | | iOS | 14.0+ | | tvOS | 14.0+ | | watchOS | 7.0+ | | visionOS | 1.0+ | | Linux | Ubuntu 18.04+ | | Windows | 10+ |
Quick Start
1. Choose Your Authentication Method
MistKit supports three credential types via the Credentials value. The service does not carry a database β each operation picks its database (and signing method, for the public database) at the call site.
API Token (read-only against the public database)
import MistKit
let credentials = try Credentials(
apiAuth: APICredentials(
apiToken: ProcessInfo.processInfo.environment["CLOUDKIT_API_TOKEN"]!
)
)
let service = CloudKitService(
containerIdentifier: "iCloud.com.example.MyApp",
credentials: credentials
)Web Authentication (user-context routes, private/shared database)
let credentials = try Credentials(
apiAuth: APICredentials(
apiToken: ProcessInfo.processInfo.environment["CLOUDKIT_API_TOKEN"]!,
webAuthToken: userWebAuthToken
)
)
let service = CloudKitService(
containerIdentifier: "iCloud.com.example.MyApp",
credentials: credentials
)Server-to-Server (public database only)
let credentials = try Credentials(
serverToServer: ServerToServerCredentials(
keyID: ProcessInfo.processInfo.environment["CLOUDKIT_KEY_ID"]!,
privateKey: .file(path: "private_key.pem")
)
)
let service = CloudKitService(
containerIdentifier: "iCloud.com.example.MyApp",
credentials: credentials,
environment: .production
)Provide both apiAuth and serverToServer to a single Credentials when one service must hit public-database routes via S2S signing and user-context routes via web-auth β MistKit picks the appropriate token manager per call.
2. Call an Operation (database chosen per call)
let records = try await service.queryRecords(
recordType: "Post",
database: .public(.prefers(.serverToServer))
)Database.public carries a PublicAuthPreference: .prefers(.serverToServer) / .prefers(.webAuth) (fall back if not configured) or .requires(.serverToServer) / .requires(.webAuth) (throw if not configured). Private/shared always use web-auth.
Usage
Authentication
API Token Authentication
- Get API Token:
- Log into Apple Developer Console - Navigate to CloudKit Database - Generate an API Token
- Set Environment Variable:
``bash export CLOUDKIT_API_TOKEN="your_api_token_here" ``
- Use in Code:
``swift let credentials = try Credentials( apiAuth: APICredentials( apiToken: ProcessInfo.processInfo.environment["CLOUDKIT_API_TOKEN"]! ) ) let service = CloudKitService( containerIdentifier: "iCloud.com.example.MyApp", credentials: credentials ) ``
Web Authentication
Web authentication enables user-specific operations and requires both an API token and a web authentication token. The token can be obtained either through CloudKit JS authentication (browser flow) or from an iOS/macOS app via CKFetchWebAuthTokenOperation, which exchanges the user's existing iCloud session for a token your backend can use.
let credentials = try Credentials(
apiAuth: APICredentials(apiToken: apiToken, webAuthToken: webAuthToken)
)
let service = CloudKitService(
containerIdentifier: "iCloud.com.example.MyApp",
credentials: credentials
)Server-to-Server Authentication
Server-to-server authentication provides enterprise-level access using ECDSA P-256 key signing. Note that this method only supports the public database.
- Generate Key Pair:
```bash # Generate private key openssl ecparam -genkey -name prime256v1 -noout -out private_key.pem
# Extract public key openssl ec -in private_key.pem -pubout -out public_key.pem ```
- Upload Public Key: Upload the public key to Apple Developer Console
- Use in Code (the simplest path β
Credentialsresolves the PEM at first use):
```swift let credentials = try Credentials( serverToServer: ServerToServerCredentials( keyID: "your_key_id", privateKey: .file(path: "private_key.pem") ) ) let service = CloudKitService( containerIdentifier: "iCloud.com.example.MyApp", credentials: credentials, environment: .production )
// Each call selects its database scope explicitly: let records = try await service.queryRecords( recordType: "Post", database: .public(.requires(.serverToServer)) ) ```
To plug in a custom TokenManager (e.g. with shared connection pooling), use the tokenManager: initializer instead:
``swift let pemString = try String(contentsOfFile: "private_key.pem", encoding: .utf8) let serverManager = try ServerToServerAuthManager( keyID: "your_key_id", pemString: pemString ) let service = CloudKitService( containerIdentifier: "iCloud.com.example.MyApp", tokenManager: serverManager, environment: .production ) ``
Error Handling
MistKit provides comprehensive error handling with typed errors:
do {
let credentials = try Credentials(
apiAuth: APICredentials(apiToken: apiToken)
)
let service = CloudKitService(
containerIdentifier: "iCloud.com.example.MyApp",
credentials: credentials
)
// Perform operations β each call picks its database, e.g.:
let posts = try await service.queryRecords(
recordType: "Post",
database: .public(.prefers(.serverToServer))
)
} catch let error as CloudKitError {
print("CloudKit error: \\(error.localizedDescription)")
} catch let error as TokenManagerError {
print("Authentication error: \\(error.localizedDescription)")
} catch let error as CredentialsValidationError {
print("Credentials error: \\(error.localizedDescription)")
} catch {
print("Unexpected error: \\(error)")
}Error Types
CloudKitError: CloudKit Web Services API errors (typed throws on every operation)CredentialsValidationError: Surfaces whenCredentials.initis called with neitherapiAuthnorserverToServerTokenManagerError: Authentication and credential errorsTokenStorageError: Token storage and persistence errors
Advanced Usage
More Operations
Beyond querying and CRUD, MistKit covers zones, subscriptions, push tokens, and asset re-referencing. Every call takes an explicit database:.
// Zones
let zone = try await service.createZone(
zoneName: "Notes",
database: .private
)
try await service.deleteZone(zoneName: "Notes", database: .private)
// Subscriptions
let subs = try await service.listSubscriptions(database: .private)
let one = try await service.lookupSubscriptions(ids: ["sub-1"], database: .private)
// Create/update/delete via service.modifySubscriptions(_:database:)
// (takes [SubscriptionOperation], returns [SubscriptionResult]).
// APNs push tokens
let token = try await service.createAPNsToken(
environment: .development,
database: .private
)
try await service.registerAPNsToken(
token.apnsToken,
environment: .development,
database: .private
)
// Re-reference existing CDN assets without re-uploading bytes
let assets = try await service.rereferenceAssets(
[(recordName: "rec-1", fieldName: "photo")],
database: .private
)Auto-Chunking Conveniences
CloudKit caps batch requests at 200 items. lookupAllRecords and the lookupInfos: form of discoverAllUserIdentities split oversized inputs into β€maxRecordsPerRequest (200) batches automatically and concatenate the results in input order β no manual chunking required.
let records = try await service.lookupAllRecords(
recordNames: thousandsOfNames, // chunked into 200-item requests
database: .private
)
let identities = try await service.discoverAllUserIdentities(
lookupInfos: manyLookupInfos,
batchSize: 200
)HTTP Transport
Non-WASI platforms default to URLSessionTransport β no transport plumbing is required. On Apple platforms, the default convenience initializer used in the examples above wires up URLSessionTransport automatically.
WASI builds use the generic, transport-accepting initializer; see Sources/MistKit/CloudKitService/CloudKitService+Initialization.swift for the internal entry point. A custom transport on Apple platforms (e.g. for server-side Swift with AsyncHTTPClient) is not yet exposed in the public v1.0.0-beta surface β track via the project roadmap.
Adaptive Token Manager
For applications that might upgrade from API-only to web authentication:
let adaptiveManager = AdaptiveTokenManager(
apiToken: apiToken,
storage: storage
)
// Later, upgrade to web authentication
try await adaptiveManager.upgradeToWebAuthentication(webAuthToken: webToken)Examples
Check out the Examples/ directory for complete working examples:
- MistDemo: Web-based CloudKit authentication demo with automatic token capture
- BushelCloud: Server-to-Server auth demo syncing macOS restore images, Xcode, and Swift versions β backend for the Bushel app
- CelestraCloud: RSS reader demonstrating CloudKit query filtering, sorting, and web etiquette patterns β backend for the Celestra app, built with SyndiKit
Documentation
- API Documentation: Complete API reference
Apple References
- CloudKit Web Services: Official CloudKit Web Services REST API documentation
- CloudKit framework: On-device CloudKit framework (iOS/macOS)
- CloudKit JS: Browser-based CloudKit access used for web auth token capture
- CKFetchWebAuthTokenOperation: iOS/macOS API for exchanging an iCloud session for a web auth token
Related Swift Packages
- swift-openapi-generator: Generates type-safe Swift clients from OpenAPI specs
- swift-openapi-async-http-client: AsyncHTTPClient transport for OpenAPI clients
- AsyncHTTPClient: HTTP client for server-side Swift
- swift-crypto: Cross-platform crypto used for ECDSA P-256 server-to-server signing
License
MistKit is released under the MIT License. See LICENSE for details.
Acknowledgments
- Built on Swift OpenAPI Generator
- Uses Swift Crypto for server-to-server authentication
- Inspired by CloudKit Web Services REST API
Roadmap
v1.0.0-alpha.1
- [x] Composing Web Service Requests β
- [x] Modifying Records (records/modify) β
- [x] Fetching Records Using a Query (records/query) β
- [x] Fetching Records by Record Name (records/lookup) β
- [x] Fetching Current User Identity (users/caller) β
v1.0.0-alpha.2
- [x] Vapor Token Client β
- [x] Vapor Token Storage β
- [x] Vapor URL Client β
- [x] Swift NIO URL Client β
- [x] Date Field Types β
- [x] Location Field Types β
- [x] List Field Types β
- [x] Reference Field Types β
- [x] Error Codes β
- [x] Fetching Zones (zones/list) β
v1.0.0-alpha.3
v1.0.0-alpha.4
- [x] Uploading Assets (assets/upload) β
- [x] Add Support for swiftlang/swift-source-compat-suite β
- [x] MistDemo - Web Authentication Demo β
- [x] WASM Platform Support β
- [x] Android Platform Support β
v1.0.0-alpha.5
- [x] Name Component Types β
- [x] Discovering User Identities (POST users/discover) β
- [x] Fetching Record Changes (records/changes) β
- [x] Fetching Zones by Identifier (zones/lookup) β
- [x] Fetching Zone Changes (zones/changes) β
- [x] Fix QueryFilter IN/NOT_IN serialization β
v1.0.0-beta.1
Querying & Sync
- [x] Query pagination with continuation markers (#306) β
- [x] Operation classification & batch sync result tracking (#296) β
Authentication
- [x]
AuthenticationMiddlewarerefactor β eachAuthenticatorapplies itself (#294) β - [x] Strengthened environment & database configuration validation (#293) β
Error Handling
- [x] Typed
TokenManagerErrorand safeRecordOperationconversion (#305) β - [x] Move
CloudKitResponseTypedefaults to protocol extension (#292) β
Concurrency
- [x] Replace custom
AsyncChannelwithswift-async-algorithms(#280) β
MistDemo
- [x]
--databaseflag anddemo-errorscommand (#282) β - [x] Test split, CRUD commands, auth fix, native app (#271 / #273) β
- [x]
IntegrationTestRunnerrefactored into protocol-based phase pipeline (#283) β
Tooling & CI
- [x] Test suite improvements (#286 / #287) β
- [x] CI updates for May 2026 (#277) β
- [x] Fail lint job when any command fails (#303) β
v1.0.0-beta.2
- [x] Referencing Existing Assets (assets/rereference) β
- [x] Modifying Zones (zones/modify) β
- [x] Fetching Subscriptions (subscriptions/list) β
- [x] Fetching Subscriptions by Identifier (subscriptions/lookup) β
- [x] Modifying Subscriptions (subscriptions/modify) β
- [x] Creating APNs Tokens (tokens/create) β
- [x] Registering Tokens (tokens/register) β
- [x] Fetching Users by Email (users/lookup/email) β
(Apple-deprecated β prefer
discoverAllUserIdentities) - [x] Fetching Users by Record Name (users/lookup/id) β
(Apple-deprecated β prefer
discoverAllUserIdentities) - [x] Auto-chunking conveniences for batch operations (#389) β
Backlog / Post-beta
- [ ] Discovering All User Identities (GET users/discover)
- [ ] Fetching Contacts (users/lookup/contacts)
- [ ] Fetching Record Information (records/resolve)
- [ ] Accepting Share Records (records/accept)
- [ ] Fetching Database Changes (changes/database)
- [ ] Fetching Record Zone Changes (changes/zone)
- [ ] Feature: Add custom CloudKit zone support for queries
v1.0.0
- [ ] System Field Integration
- [ ] Handle Data Size Limits
- [ ] Add architecture diagrams to Bushel documentation
- [ ] Add comprehensive test suite for Bushel demo
- [ ] Implement incremental sync with change tracking for Bushel
- [ ] Migrate Bushel Demo to it's own Repository
- [ ] Migrate Celestra Demo to it's own Repository
v1.1.0
Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: API Reference
MistKit: Bringing CloudKit to every Swift platform π
Package Metadata
Repository: brightdigit/mistkit
Default branch: main
README: README.md