2
votes

I am fetching record changes from CloudKit Server but each time I get error

[LogFacilityCK] Got a connection error for operation E0367CA861DAF5A9: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.apple.cloudd" UserInfo={NSDebugDescription=connection to service named com.apple.cloudd}

I know the reason as I am wrongly initialise CKFetchRecordZoneChangesOperation Please check the following code and correct me:

 CKFetchRecordZoneChangesOperation  *operation = [[CKFetchRecordZoneChangesOperation alloc]initWithRecordZoneIDs:@[ recordZoneID ] optionsByRecordZoneID:@{@"Zone":recordZoneID,@"FetchRecordZoneChangesOptions":option}];

I need help on this part:

**@{@"Zone":recordZoneID,@"FetchRecordZoneChangesOptions":option}];**
2

2 Answers

5
votes

Simply create variable:

var serverChangeToken: CKServerChangeToken? {

    guard let encoded = UserDefaults(suiteName: SharedGroupName)?.data(forKey: ServerChangeTokenKey) else {
        return nil
    }

    return NSKeyedUnarchiver.unarchiveObject(with: encoded) as? CKServerChangeToken
}

And then init an object:

        let zone = CKRecordZone(zoneName: "my_custom_zone_name")
        let options = CKFetchRecordZoneChangesOptions()
        options.previousServerChangeToken = serverChangeToken

        let operation = CKFetchRecordZoneChangesOperation(recordZoneIDs: [zone.zoneID], optionsByRecordZoneID: [zone.zoneID: options])
2
votes

Even by using the right CKServerChangeToken, I was encountering this error because I didn't control the case where the array changedZoneIDs has 0 elements.

Strictly speaking, you need two separate CKServerChangeToken to access the database changes and the zoneChanges (let's say databaseChangeToken and zoneChangeToken). You can create them as Bartłomiej Semańczyk explains

-use recordZoneWithIDChangedBlock to get the zoneID of the changed zones
-use fetchDatabaseChangesCompletionBlock to save the databaseChangeToken and to call the CKFetchRecordZoneChangesOperation where you use the zoneChangeToken,
-each zone has its own changeToken so I ended up with this solution:

let operation = CKFetchDatabaseChangesOperation(previousServerChangeToken: databaseChangeToken)
var changedZoneIDs: [CKRecordZoneID] = []

operation.recordZoneWithIDChangedBlock = { (zoneID) in
    changedZoneIDs.append(zoneID)
}

operation.fetchDatabaseChangesCompletionBlock = { (databaseToken, moreComing, error) in
{
    //manage moreComing
    //save databaseToken
    //manage error

    if changedZoneIDs.count > 0
    {
        var optionsByRecordZoneID = [CKRecordZoneID: CKFetchRecordZoneChangesOptions]()
        for zoneID in changedZoneIDs {
            let changeTokenData = UserDefaults.standard.value(forKey: "\(zoneID.zoneName) zoneChangeToken") as? Data // Read change token from disk
            var zoneChangeToken:CKServerChangeToken?

            if (changeTokenData != nil){
                zoneChangeToken = NSKeyedUnarchiver.unarchiveObject(with: changeTokenData!)as! CKServerChangeToken?
            }
            let options = CKFetchRecordZoneChangesOptions()
            options.previousServerChangeToken = zoneChangeToken
            optionsByRecordZoneID[zoneID] = options
        }
        let operation = CKFetchRecordZoneChangesOperation(recordZoneIDs: changedZoneIDs, optionsByRecordZoneID: optionsByRecordZoneID)

    // ...
    }