0

I have a "like button" and all the users use it at the same time. It should increment the value of like record in the cloud container by one, or decrease it by one. How can I guarantee that the update happens to the last version of the record? I mean what is the code in Swift 3 that can guarantee that every user will get the final value of the like record and apply the increment while other users send the same operation on the same record? Sorry I don't have code to show about this.

I read about CKErrorServerRecordChanged but I don't know how to use it in syntax. If you have an example for the code will be, it will be great.

I found it in Firebase. It is called transaction and it keeps tracking the record version until it applies the operation to the last version so it will not lose any of the user operation targeting that record.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Michsedki
  • 1
  • 2

1 Answers1

1

Ok, not sure this is a cut'n'paste answer, but will give you a template to work with. It shows extensive error code handling, with an action next to the retry with the save action for example. You should be able work out how to do what you want to do with this code.

func files_saveSet() {
    let newRecord = CKRecord(recordType: "Blah", recordID: sharedDataAccess.iCloudID)
    newRecord["Key"] = sharedDataAccess.iCloudLink as CKRecordValue?

    var localChanges:[CKRecord] = []
    let records2Erase:[CKRecordID] = []
    localChanges.append(newRecord)

    let saveRecordsOperation = CKModifyRecordsOperation(recordsToSave: localChanges, recordIDsToDelete: records2Erase)
    saveRecordsOperation.savePolicy = .changedKeys
    saveRecordsOperation.modifyRecordsCompletionBlock = { savedRecords, deletedRecordIDs, error in
        self.theApp.isNetworkActivityIndicatorVisible = false
        guard error == nil else {
            if let ckerror = error as? CKError {
                if ckerror.code == CKError.requestRateLimited {
                    let retryInterval = ckerror.userInfo[CKErrorRetryAfterKey] as? TimeInterval
                        DispatchQueue.main.async {
                            Timer.scheduledTimer(timeInterval: retryInterval!, target: self, selector: #selector(self.files_saveSet), userInfo: nil, repeats: false)
                        }
                    } else if ckerror.code == CKError.zoneBusy {
                        let retryInterval = ckerror.userInfo[CKErrorRetryAfterKey] as? TimeInterval
                            DispatchQueue.main.async {
                                Timer.scheduledTimer(timeInterval: retryInterval!, target: self, selector: #selector(self.files_saveSet), userInfo: nil, repeats: false)
                            }
                        } else if ckerror.code == CKError.limitExceeded {
                            let retryInterval = ckerror.userInfo[CKErrorRetryAfterKey] as? TimeInterval
                                DispatchQueue.main.async {
                                    Timer.scheduledTimer(timeInterval: retryInterval!, target: self, selector: #selector(self.files_saveSet), userInfo: nil, repeats: false)
                                }
                            } else if ckerror.code == CKError.notAuthenticated {
                                NotificationCenter.default.post(name: Notification.Name("noCloud"), object: nil, userInfo: nil)
                                } else if ckerror.code == CKError.networkFailure {
                                    NotificationCenter.default.post(name: Notification.Name("networkFailure"), object: nil, userInfo: nil)
                                        } else if ckerror.code == CKError.networkUnavailable {
                                        NotificationCenter.default.post(name: Notification.Name("noWiFi"), object: nil, userInfo: nil)
                                        } else if ckerror.code == CKError.quotaExceeded {
                                            NotificationCenter.default.post(name: Notification.Name("quotaExceeded"), object: nil, userInfo: nil)
                                        } else if ckerror.code == CKError.partialFailure {
                                            NotificationCenter.default.post(name: Notification.Name("partialFailure"), object: nil, userInfo: nil)
                                        } else if (ckerror.code == CKError.internalError || ckerror.code == CKError.serviceUnavailable) {
                                            NotificationCenter.default.post(name: Notification.Name("serviceUnavailable"), object: nil, userInfo: nil)
                                        }
                } // end of guard statement
            return
        }

        if error != nil {
                //print(error!.localizedDescription)
        } else {
                //print("ok")
        }
    }

    saveRecordsOperation.qualityOfService = .background
    privateDB.add(saveRecordsOperation)
    theApp.isNetworkActivityIndicatorVisible = true
}
user3069232
  • 8,587
  • 7
  • 46
  • 87