cloudkit 0.3.8

Safe Rust bindings for Apple's CloudKit framework — iCloud databases and sync on macOS
Documentation
import CloudKit
import Foundation

@_cdecl("ck_database_save_record_sync")
public func ckDatabaseSaveRecordSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ databaseScope: Int32,
    _ recordJSON: UnsafePointer<CChar>?,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let database = try ckMakeDatabase(containerIdentifier: containerIdentifier, scopeRaw: databaseScope)
        let payload = try ckDecodeJSON(recordJSON, as: CKRecordPayload.self)
        let record = try ckDecodeRecord(payload)
        let savedRecord = try ckAwait(label: "CloudKit saveRecord") { completion in
            database.save(record) { savedRecord, error in
                completion(savedRecord, error as NSError?)
            }
        }
        outJSON?.pointee = ckCString(try ckEncodeJSON(try ckEncodeRecord(savedRecord)))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_database_fetch_record_sync")
public func ckDatabaseFetchRecordSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ databaseScope: Int32,
    _ recordIDJSON: UnsafePointer<CChar>?,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let database = try ckMakeDatabase(containerIdentifier: containerIdentifier, scopeRaw: databaseScope)
        let recordIDPayload = try ckDecodeJSON(recordIDJSON, as: CKRecordIDPayload.self)
        let fetchedRecord = try ckAwait(label: "CloudKit fetchRecord") { completion in
            database.fetch(withRecordID: ckDecodeRecordID(recordIDPayload)) { record, error in
                completion(record, error as NSError?)
            }
        }
        outJSON?.pointee = ckCString(try ckEncodeJSON(try ckEncodeRecord(fetchedRecord)))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_database_delete_record_sync")
public func ckDatabaseDeleteRecordSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ databaseScope: Int32,
    _ recordIDJSON: UnsafePointer<CChar>?,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let database = try ckMakeDatabase(containerIdentifier: containerIdentifier, scopeRaw: databaseScope)
        let recordIDPayload = try ckDecodeJSON(recordIDJSON, as: CKRecordIDPayload.self)
        let deletedRecordID = try ckAwait(label: "CloudKit deleteRecord") { completion in
            database.delete(withRecordID: ckDecodeRecordID(recordIDPayload)) { recordID, error in
                completion(recordID, error as NSError?)
            }
        }
        outJSON?.pointee = ckCString(try ckEncodeJSON(ckEncodeRecordID(deletedRecordID)))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_database_perform_query_sync")
public func ckDatabasePerformQuerySync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ databaseScope: Int32,
    _ queryJSON: UnsafePointer<CChar>?,
    _ zoneIDJSON: UnsafePointer<CChar>?,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let database = try ckMakeDatabase(containerIdentifier: containerIdentifier, scopeRaw: databaseScope)
        let query = ckDecodeQuery(try ckDecodeJSON(queryJSON, as: CKQueryPayload.self))
        let zoneID = try zoneIDJSON.map { try ckDecodeJSON($0, as: CKRecordZoneIDPayload.self) }.map(ckDecodeZoneID)
        let records = try ckAwait(label: "CloudKit performQuery") { completion in
            database.fetch(withQuery: query, inZoneWith: zoneID, desiredKeys: nil, resultsLimit: CKQueryOperation.maximumResults) { result in
                switch result {
                case .success(let output):
                    let records = output.matchResults.compactMap { try? $0.1.get() }
                    completion(records, nil)
                case .failure(let error):
                    completion(nil, error as NSError)
                }
            }
        }
        let payloads = try records.map(ckEncodeRecord)
        outJSON?.pointee = ckCString(try ckEncodeJSON(payloads))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_database_perform_query_async")
public func ckDatabasePerformQueryAsync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ databaseScope: Int32,
    _ queryJSON: UnsafePointer<CChar>?,
    _ zoneIDJSON: UnsafePointer<CChar>?,
    _ callback: @escaping CKJSONStringCallback,
    _ refcon: UnsafeMutableRawPointer?
) {
    let boxPtr = ckRetain(CKJSONStringCallbackBox(callback: callback, refcon: refcon))
    do {
        let database = try ckMakeDatabase(containerIdentifier: containerIdentifier, scopeRaw: databaseScope)
        let query = ckDecodeQuery(try ckDecodeJSON(queryJSON, as: CKQueryPayload.self))
        let zoneID = try zoneIDJSON.map { try ckDecodeJSON($0, as: CKRecordZoneIDPayload.self) }.map(ckDecodeZoneID)
        database.fetch(withQuery: query, inZoneWith: zoneID, desiredKeys: nil, resultsLimit: CKQueryOperation.maximumResults) { result in
            let box: CKJSONStringCallbackBox = ckUnretained(boxPtr, as: CKJSONStringCallbackBox.self)
            switch result {
            case .success(let output):
                let records = output.matchResults.compactMap { try? $0.1.get() }
                let payloads = (try? records.map(ckEncodeRecord)) ?? []
                let json = (try? ckEncodeJSON(payloads)) ?? "[]"
                box.succeed(json: json)
            case .failure(let error):
                box.fail(error: error as NSError)
            }
            ckRelease(boxPtr)
        }
    } catch let error as NSError {
        let box: CKJSONStringCallbackBox = ckUnretained(boxPtr, as: CKJSONStringCallbackBox.self)
        box.fail(error: error)
        ckRelease(boxPtr)
    }
}

@_cdecl("ck_database_fetch_query_results_sync")
public func ckDatabaseFetchQueryResultsSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ databaseScope: Int32,
    _ queryJSON: UnsafePointer<CChar>?,
    _ zoneIDJSON: UnsafePointer<CChar>?,
    _ desiredKeysJSON: UnsafePointer<CChar>?,
    _ resultsLimit: Int32,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let database = try ckMakeDatabase(containerIdentifier: containerIdentifier, scopeRaw: databaseScope)
        let query = ckDecodeQuery(try ckDecodeJSON(queryJSON, as: CKQueryPayload.self))
        let zoneID = try zoneIDJSON.map { try ckDecodeJSON($0, as: CKRecordZoneIDPayload.self) }.map(ckDecodeZoneID)
        let desiredKeys = try desiredKeysJSON.map { try ckDecodeJSON($0, as: [String].self) }
        let limit = resultsLimit > 0 ? Int(resultsLimit) : CKQueryOperation.maximumResults
        let results = try ckAwait(label: "CloudKit fetch query results") { completion in
            database.fetch(withQuery: query, inZoneWith: zoneID, desiredKeys: desiredKeys, resultsLimit: limit) { result in
                switch result {
                case .success(let output):
                    completion(output, nil)
                case .failure(let error):
                    completion(nil, error as NSError)
                }
            }
        }

        let records = try results.matchResults.compactMap { try? $0.1.get() }.map(ckEncodeRecord)
        let matches = try results.matchResults.map { recordID, result in
            switch result {
            case .success(let record):
                return CKQueryMatchResultPayload(recordID: ckEncodeRecordID(recordID), record: try ckEncodeRecord(record), error: nil)
            case .failure(let error):
                return CKQueryMatchResultPayload(recordID: ckEncodeRecordID(recordID), record: nil, error: ckErrorPayload(from: error as NSError))
            }
        }
        let payload = CKFetchedQueryResultsPayload(
            records: records,
            matches: matches,
            cursor: results.queryCursor.map(ckEncodeQueryCursor),
            operationError: nil
        )
        outJSON?.pointee = ckCString(try ckEncodeJSON(payload))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_database_fetch_all_record_zones_sync")
public func ckDatabaseFetchAllRecordZonesSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ databaseScope: Int32,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let database = try ckMakeDatabase(containerIdentifier: containerIdentifier, scopeRaw: databaseScope)
        let zones = try ckAwait(label: "CloudKit fetch all record zones") { completion in
            database.fetchAllRecordZones { zones, error in
                completion(zones, error as NSError?)
            }
        }
        outJSON?.pointee = ckCString(try ckEncodeJSON(zones.map(ckEncodeZone)))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_database_fetch_record_zone_sync")
public func ckDatabaseFetchRecordZoneSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ databaseScope: Int32,
    _ zoneIDJSON: UnsafePointer<CChar>?,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let database = try ckMakeDatabase(containerIdentifier: containerIdentifier, scopeRaw: databaseScope)
        let zoneIDPayload = try ckDecodeJSON(zoneIDJSON, as: CKRecordZoneIDPayload.self)
        let zone = try ckAwait(label: "CloudKit fetch record zone") { completion in
            database.fetch(withRecordZoneID: ckDecodeZoneID(zoneIDPayload)) { zone, error in
                completion(zone, error as NSError?)
            }
        }
        outJSON?.pointee = ckCString(try ckEncodeJSON(ckEncodeZone(zone)))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_database_save_record_zone_sync")
public func ckDatabaseSaveRecordZoneSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ databaseScope: Int32,
    _ zoneJSON: UnsafePointer<CChar>?,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let database = try ckMakeDatabase(containerIdentifier: containerIdentifier, scopeRaw: databaseScope)
        let zonePayload = try ckDecodeJSON(zoneJSON, as: CKRecordZonePayload.self)
        let zone = try ckAwait(label: "CloudKit save record zone") { completion in
            database.save(ckDecodeZone(zonePayload)) { zone, error in
                completion(zone, error as NSError?)
            }
        }
        outJSON?.pointee = ckCString(try ckEncodeJSON(ckEncodeZone(zone)))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_database_delete_record_zone_sync")
public func ckDatabaseDeleteRecordZoneSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ databaseScope: Int32,
    _ zoneIDJSON: UnsafePointer<CChar>?,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let database = try ckMakeDatabase(containerIdentifier: containerIdentifier, scopeRaw: databaseScope)
        let zoneIDPayload = try ckDecodeJSON(zoneIDJSON, as: CKRecordZoneIDPayload.self)
        let deletedZoneID = try ckAwait(label: "CloudKit delete record zone") { completion in
            database.delete(withRecordZoneID: ckDecodeZoneID(zoneIDPayload)) { zoneID, error in
                completion(zoneID, error as NSError?)
            }
        }
        outJSON?.pointee = ckCString(try ckEncodeJSON(ckEncodeZoneID(deletedZoneID)))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_database_fetch_subscription_sync")
public func ckDatabaseFetchSubscriptionSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ databaseScope: Int32,
    _ subscriptionID: UnsafePointer<CChar>?,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        guard let subscriptionID else {
            throw ckBridgeNSError(code: CKR_INVALID_ARGUMENT, message: "Missing subscription ID")
        }
        let database = try ckMakeDatabase(containerIdentifier: containerIdentifier, scopeRaw: databaseScope)
        let subscription = try ckAwait(label: "CloudKit fetch subscription") { completion in
            database.fetch(withSubscriptionID: String(cString: subscriptionID)) { subscription, error in
                completion(subscription, error as NSError?)
            }
        }
        outJSON?.pointee = ckCString(try ckEncodeJSON(ckEncodeSubscription(subscription)))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_database_fetch_all_subscriptions_sync")
public func ckDatabaseFetchAllSubscriptionsSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ databaseScope: Int32,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let database = try ckMakeDatabase(containerIdentifier: containerIdentifier, scopeRaw: databaseScope)
        let subscriptions = try ckAwait(label: "CloudKit fetch all subscriptions") { completion in
            database.fetchAllSubscriptions { subscriptions, error in
                completion(subscriptions, error as NSError?)
            }
        }
        outJSON?.pointee = ckCString(try ckEncodeJSON(subscriptions.map(ckEncodeSubscription)))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_database_save_subscription_sync")
public func ckDatabaseSaveSubscriptionSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ databaseScope: Int32,
    _ subscriptionJSON: UnsafePointer<CChar>?,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let database = try ckMakeDatabase(containerIdentifier: containerIdentifier, scopeRaw: databaseScope)
        let subscriptionPayload = try ckDecodeJSON(subscriptionJSON, as: CKSubscriptionPayload.self)
        let savedSubscription = try ckAwait(label: "CloudKit save subscription") { completion in
            database.save(ckDecodeSubscription(subscriptionPayload)) { subscription, error in
                completion(subscription, error as NSError?)
            }
        }
        outJSON?.pointee = ckCString(try ckEncodeJSON(ckEncodeSubscription(savedSubscription)))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_database_delete_subscription_sync")
public func ckDatabaseDeleteSubscriptionSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ databaseScope: Int32,
    _ subscriptionID: UnsafePointer<CChar>?,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        guard let subscriptionID else {
            throw ckBridgeNSError(code: CKR_INVALID_ARGUMENT, message: "Missing subscription ID")
        }
        let database = try ckMakeDatabase(containerIdentifier: containerIdentifier, scopeRaw: databaseScope)
        let deletedSubscriptionID = try ckAwait(label: "CloudKit delete subscription") { completion in
            database.delete(withSubscriptionID: String(cString: subscriptionID)) { subscriptionID, error in
                completion(subscriptionID, error as NSError?)
            }
        }
        outJSON?.pointee = ckCString(try ckEncodeJSON(deletedSubscriptionID))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}