cloudkit 0.3.4

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

@_cdecl("ck_container_account_status_sync")
public func ckContainerAccountStatusSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ outStatus: UnsafeMutablePointer<Int32>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let container = try ckMakeContainer(containerIdentifier)
        let status = try ckAwait(label: "CloudKit account status") { completion in
            container.accountStatus { status, error in
                completion(status, error as NSError?)
            }
        }
        outStatus?.pointee = Int32(status.rawValue)
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_container_account_status_async")
public func ckContainerAccountStatusAsync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ callback: @escaping CKAccountStatusCallback,
    _ refcon: UnsafeMutableRawPointer?
) {
    let boxPtr = ckRetain(CKAccountStatusCallbackBox(callback: callback, refcon: refcon))
    do {
        let container = try ckMakeContainer(containerIdentifier)
        container.accountStatus { status, error in
            let box: CKAccountStatusCallbackBox = ckUnretained(boxPtr, as: CKAccountStatusCallbackBox.self)
            box.complete(status: status, error: error as NSError?)
            ckRelease(boxPtr)
        }
    } catch let error as NSError {
        let box: CKAccountStatusCallbackBox = ckUnretained(boxPtr, as: CKAccountStatusCallbackBox.self)
        box.complete(status: .couldNotDetermine, error: error)
        ckRelease(boxPtr)
    }
}

@_cdecl("ck_container_fetch_user_record_id_sync")
public func ckContainerFetchUserRecordIDSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let container = try ckMakeContainer(containerIdentifier)
        let recordID = try ckAwait(label: "CloudKit user record ID") { completion in
            container.fetchUserRecordID { recordID, error in
                completion(recordID, error as NSError?)
            }
        }
        outJSON?.pointee = ckCString(try ckEncodeJSON(ckEncodeRecordID(recordID)))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_container_fetch_user_record_id_async")
public func ckContainerFetchUserRecordIDAsync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ callback: @escaping CKJSONStringCallback,
    _ refcon: UnsafeMutableRawPointer?
) {
    let boxPtr = ckRetain(CKJSONStringCallbackBox(callback: callback, refcon: refcon))
    do {
        let container = try ckMakeContainer(containerIdentifier)
        container.fetchUserRecordID { recordID, error in
            let box: CKJSONStringCallbackBox = ckUnretained(boxPtr, as: CKJSONStringCallbackBox.self)
            if let error = error as NSError? {
                box.fail(error: error)
            } else if let recordID {
                let json = (try? ckEncodeJSON(ckEncodeRecordID(recordID))) ?? "{}"
                box.succeed(json: json)
            } else {
                box.fail(error: ckBridgeNSError(code: CKR_FAILURE, message: "CloudKit returned a nil user record ID"))
            }
            ckRelease(boxPtr)
        }
    } catch let error as NSError {
        let box: CKJSONStringCallbackBox = ckUnretained(boxPtr, as: CKJSONStringCallbackBox.self)
        box.fail(error: error)
        ckRelease(boxPtr)
    }
}

private func ckAwaitUserIdentity(
    container: CKContainer,
    lookupInfo: CKUserIdentityLookupInfoPayload
) throws -> CKUserIdentity {
    if let emailAddress = lookupInfo.emailAddress {
        return try ckAwait(label: "CloudKit discover user identity") { completion in
            container.discoverUserIdentity(withEmailAddress: emailAddress) { identity, error in
                completion(identity, error as NSError?)
            }
        }
    }
    if let phoneNumber = lookupInfo.phoneNumber {
        return try ckAwait(label: "CloudKit discover user identity") { completion in
            container.discoverUserIdentity(withPhoneNumber: phoneNumber) { identity, error in
                completion(identity, error as NSError?)
            }
        }
    }
    if let userRecordID = lookupInfo.userRecordID {
        return try ckAwait(label: "CloudKit discover user identity") { completion in
            container.discoverUserIdentity(withUserRecordID: ckDecodeRecordID(userRecordID)) { identity, error in
                completion(identity, error as NSError?)
            }
        }
    }
    throw ckBridgeNSError(code: CKR_INVALID_ARGUMENT, message: "User identity lookup info must include an email address, phone number, or user record ID")
}

private func ckAwaitShareParticipant(
    container: CKContainer,
    lookupInfo: CKUserIdentityLookupInfoPayload
) throws -> CKShare.Participant {
    if let emailAddress = lookupInfo.emailAddress {
        return try ckAwait(label: "CloudKit fetch share participant") { completion in
            container.fetchShareParticipant(withEmailAddress: emailAddress) { participant, error in
                completion(participant, error as NSError?)
            }
        }
    }
    if let phoneNumber = lookupInfo.phoneNumber {
        return try ckAwait(label: "CloudKit fetch share participant") { completion in
            container.fetchShareParticipant(withPhoneNumber: phoneNumber) { participant, error in
                completion(participant, error as NSError?)
            }
        }
    }
    if let userRecordID = lookupInfo.userRecordID {
        return try ckAwait(label: "CloudKit fetch share participant") { completion in
            container.fetchShareParticipant(withUserRecordID: ckDecodeRecordID(userRecordID)) { participant, error in
                completion(participant, error as NSError?)
            }
        }
    }
    throw ckBridgeNSError(code: CKR_INVALID_ARGUMENT, message: "Share participant lookup info must include an email address, phone number, or user record ID")
}

@_cdecl("ck_container_discover_user_identity_sync")
public func ckContainerDiscoverUserIdentitySync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ lookupInfoJSON: UnsafePointer<CChar>?,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let container = try ckMakeContainer(containerIdentifier)
        let lookupInfo = try ckDecodeJSON(lookupInfoJSON, as: CKUserIdentityLookupInfoPayload.self)
        let identity = try ckAwaitUserIdentity(container: container, lookupInfo: lookupInfo)
        outJSON?.pointee = ckCString(try ckEncodeJSON(ckEncodeUserIdentity(identity)))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}

@_cdecl("ck_container_fetch_share_participant_sync")
public func ckContainerFetchShareParticipantSync(
    _ containerIdentifier: UnsafePointer<CChar>?,
    _ lookupInfoJSON: UnsafePointer<CChar>?,
    _ outJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    _ outErrorJSON: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Int32 {
    do {
        let container = try ckMakeContainer(containerIdentifier)
        let lookupInfo = try ckDecodeJSON(lookupInfoJSON, as: CKUserIdentityLookupInfoPayload.self)
        let participant = try ckAwaitShareParticipant(container: container, lookupInfo: lookupInfo)
        outJSON?.pointee = ckCString(try ckEncodeJSON(ckEncodeShareParticipant(participant)))
        return CKR_OK
    } catch let error as NSError {
        ckWriteError(error, to: outErrorJSON)
        return Int32(error.code)
    }
}