Skip to content

brightdigit/MistKit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

92 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MistKit Logo

MistKit

SwiftPM Swift Versions Platforms License GitHub Workflow Status Codecov CodeFactor Grade Maintainability Documentation

A Swift Package for Server-Side and Command-Line Access to CloudKit Web Services

Table of Contents

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.1")
]

Or add it through Xcode:

  1. File → Add Package Dependencies
  2. Enter: https://github.com/brightdigit/MistKit.git
  3. 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 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+

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

  1. Get API Token:

  2. Set Environment Variable:

    export CLOUDKIT_API_TOKEN="your_api_token_here"
  3. Use in Code:

    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.

  1. 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
  2. Upload Public Key: Upload the public key to Apple Developer Console

  3. Use in Code (the simplest path — Credentials resolves the PEM at first use):

    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:

    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 when Credentials.init is called with neither apiAuth nor serverToServer
  • TokenManagerError: Authentication and credential errors
  • TokenStorageError: Token storage and persistence errors

Advanced Usage

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

Apple References

Related Swift Packages

License

MistKit is released under the MIT License. See LICENSE for details.

Acknowledgments

Roadmap

v1.0.0-alpha.1

v1.0.0-alpha.2

v1.0.0-alpha.3

v1.0.0-alpha.4

v1.0.0-alpha.5

v1.0.0-beta.1

Querying & Sync

  • Query pagination with continuation markers (#306) ✅
  • Operation classification & batch sync result tracking (#296) ✅

Authentication

  • AuthenticationMiddleware refactor — each Authenticator applies itself (#294) ✅
  • Strengthened environment & database configuration validation (#293) ✅

Error Handling

  • Typed TokenManagerError and safe RecordOperation conversion (#305) ✅
  • Move CloudKitResponseType defaults to protocol extension (#292) ✅

Concurrency

  • Replace custom AsyncChannel with swift-async-algorithms (#280) ✅

MistDemo

  • --database flag and demo-errors command (#282) ✅
  • Test split, CRUD commands, auth fix, native app (#271 / #273) ✅
  • IntegrationTestRunner refactored into protocol-based phase pipeline (#283) ✅

Tooling & CI

  • Test suite improvements (#286 / #287) ✅
  • CI updates for May 2026 (#277) ✅
  • Fail lint job when any command fails (#303) ✅

v1.0.0-alpha.X

v1.0.0

v1.1.0

Support


MistKit: Bringing CloudKit to every Swift platform 🌟

About

Swift Package for Server-Side and Command-Line Access to CloudKit Web Services

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages