A Swift Package for Server-Side and Command-Line Access to CloudKit Web Services
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.
- 🌍 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
- 🔒 Secure: Built-in security best practices and credential management
Add MistKit to your Package.swift
file:
dependencies: [
.package(url: "https://github.com/brightdigit/MistKit.git", from: "1.0.0-alpha.1")
]
Or add it through Xcode:
- File → Add Package Dependencies
- Enter:
https://github.com/brightdigit/MistKit.git
- Select version and add to your target
MistKit supports three authentication methods depending on your use case:
import MistKit
let service = try CloudKitService(
containerIdentifier: "iCloud.com.example.MyApp",
apiToken: ProcessInfo.processInfo.environment["CLOUDKIT_API_TOKEN"]!
)
let service = try CloudKitService(
containerIdentifier: "iCloud.com.example.MyApp",
apiToken: ProcessInfo.processInfo.environment["CLOUDKIT_API_TOKEN"]!,
webAuthToken: userWebAuthToken
)
let serverManager = try ServerToServerAuthManager(
keyIdentifier: ProcessInfo.processInfo.environment["CLOUDKIT_KEY_ID"]!,
privateKeyData: privateKeyData
)
let service = try CloudKitService(
containerIdentifier: "iCloud.com.example.MyApp",
tokenManager: serverManager,
environment: .production,
database: .public
)
do {
let service = try CloudKitService(
containerIdentifier: "iCloud.com.example.MyApp",
apiToken: ProcessInfo.processInfo.environment["CLOUDKIT_API_TOKEN"]!
)
// Use service for CloudKit operations
} catch {
print("Failed to create service: \\(error)")
}
-
Get API Token:
- Log into Apple Developer Console
- Navigate to CloudKit Database
- Generate an API Token
-
Set Environment Variable:
export CLOUDKIT_API_TOKEN="your_api_token_here"
-
Use in Code:
let service = try CloudKitService( containerIdentifier: "iCloud.com.example.MyApp", apiToken: ProcessInfo.processInfo.environment["CLOUDKIT_API_TOKEN"]! )
Web authentication enables user-specific operations and requires both an API token and a web authentication token obtained through CloudKit JS authentication.
let service = try CloudKitService(
containerIdentifier: "iCloud.com.example.MyApp",
apiToken: apiToken,
webAuthToken: webAuthToken
)
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:
# 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:
let privateKeyData = try Data(contentsOf: URL(fileURLWithPath: "private_key.pem")) let serverManager = try ServerToServerAuthManager( keyIdentifier: "your_key_id", privateKeyData: privateKeyData ) let service = try CloudKitService( containerIdentifier: "iCloud.com.example.MyApp", tokenManager: serverManager, environment: .production, database: .public )
Platform | Minimum Version | Server-to-Server Auth |
---|---|---|
macOS | 10.15+ | 11.0+ |
iOS | 13.0+ | 14.0+ |
tvOS | 13.0+ | 14.0+ |
watchOS | 6.0+ | 7.0+ |
visionOS | 1.0+ | 1.0+ |
Linux | Ubuntu 18.04+ | ✅ |
Windows | 10+ | ✅ |
MistKit provides comprehensive error handling with typed errors:
do {
let service = try CloudKitService(
containerIdentifier: "iCloud.com.example.MyApp",
apiToken: apiToken
)
// Perform operations
} catch let error as CloudKitError {
print("CloudKit error: \\(error.localizedDescription)")
} catch let error as TokenManagerError {
print("Authentication error: \\(error.localizedDescription)")
} catch {
print("Unexpected error: \\(error)")
}
CloudKitError
: CloudKit Web Services API errorsTokenManagerError
: Authentication and credential errorsTokenStorageError
: Token storage and persistence errors
Always store sensitive credentials in environment variables:
# .env file (never commit this!)
CLOUDKIT_API_TOKEN=your_api_token_here
CLOUDKIT_KEY_ID=your_key_id_here
MistKit automatically masks sensitive information in logs:
// Sensitive data is automatically redacted in log output
print("Token: \\(secureToken)") // Outputs: Token: abc12345***
For persistent applications, use secure token storage:
let storage = InMemoryTokenStorage() // For development
// Use KeychainTokenStorage() for production (implement as needed)
let tokenManager = WebAuthTokenManager(
apiToken: apiToken,
webAuthToken: webAuthToken,
storage: storage
)
let service = try CloudKitService(
containerIdentifier: "iCloud.com.example.MyApp",
tokenManager: tokenManager,
environment: .development,
database: .private
)
For server-side applications, MistKit can use swift-openapi-async-http-client as the underlying HTTP transport. This is particularly useful for server-side Swift applications that need robust HTTP client capabilities.
import MistKit
import OpenAPIAsyncHTTPClient
// Create an AsyncHTTPClient instance
let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
// Create the transport
let transport = AsyncHTTPClientTransport(client: httpClient)
// Use with CloudKit service
let service = try CloudKitService(
containerIdentifier: "iCloud.com.example.MyApp",
apiToken: apiToken,
transport: transport
)
// Don't forget to shutdown the client when done
defer {
try? httpClient.syncShutdown()
}
- Production Ready: AsyncHTTPClient is battle-tested in server environments
- Performance: Optimized for high-throughput server applications
- Configuration: Extensive configuration options for timeouts, connection pooling, and more
- Platform Support: Works across all supported platforms including Linux
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.upgradeToWebAuth(webAuthToken: webToken)
Check out the Examples/
directory for complete working examples:
- Command Line Tool: Basic CloudKit operations from the command line
- Server Application: Using MistKit in a server-side Swift application
- Cross-Platform App: Shared CloudKit logic across multiple platforms
- API Documentation: Complete API reference
- DocC Documentation: Interactive documentation
- CloudKit Web Services: Apple's official CloudKit Web Services documentation
- Swift 6.1+
- Xcode 16.0+ (for iOS/macOS development)
- Linux: Ubuntu 18.04+ with Swift 6.1+
MistKit is released under the MIT License. See LICENSE for details.
- Built on Swift OpenAPI Generator
- Uses Swift Crypto for server-to-server authentication
- Inspired by CloudKit Web Services REST API
- Composing Web Service Requests ✅
- Modifying Records (records/modify) ✅
- Fetching Records Using a Query (records/query) ✅
- Fetching Records by Record Name (records/lookup) ✅
- Fetching Current User Identity (users/caller) ✅
- Vapor Token Client ✅
- Vapor Token Storage ✅
- Vapor URL Client ✅
- Swift NIO URL Client ✅
- Date Field Types ✅
- Location Field Types ✅
- System Field Integration ❌
- Name Component Types ❌
- Discovering User Identities (POST users/discover) ❌
- Discovering All User Identities (GET users/discover) ❌
- Support
postMessage
for Authentication Requests ❌ - Uploading Assets (assets/upload) ❌
- Referencing Existing Assets (assets/rereference) ❌
- Fetching Records Using a Query (records/query) w/ basic filtering ❌
- Handle Data Size Limits ❌
- Fetching Contacts (users/lookup/contacts) ❌
- Fetching Users by Email (users/lookup/email) ❌
- Fetching Users by Record Name (users/lookup/id) ❌
- Fetching Record Changes (records/changes) ❌
- Fetching Record Information (records/resolve) ❌
- Accepting Share Records (records/accept) ❌
- Fetching Zones (zones/list) ❌
- Fetching Zones by Identifier (zones/lookup) ❌
- Modifying Zones (zones/modify) ❌
- Fetching Database Changes (changes/database) ❌
- Fetching Record Zone Changes (changes/zone) ❌
- Fetching Zone Changes (zones/changes) ❌
- Fetching Subscriptions (subscriptions/list) ❌
- Fetching Subscriptions by Identifier (subscriptions/lookup) ❌
- Modifying Subscriptions (subscriptions/modify) ❌
- Creating APNs Tokens (tokens/create) ❌
- Registering Tokens (tokens/register) ❌
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: API Reference
MistKit: Bringing CloudKit to every Swift platform 🌟