I'm creating an app that saves records to CloudKit and also stores the data locally in Core Data. I can indeed save records in both locations, but I need to be able to handle errors when a connection to iCloud is not available or there is a save error. I thought I could populate a variable within the CKModifyRecordsOperation
completion block to differentiate, but that does not work - the return value from the CloudKit save is always false, even though the process succeeds. The idea is to save to CloudKit first, then retrieve the recordID and save that with the Core Data save.
Here's my code, I call doTheComboSave()
from a barbutton. The console output is below the code. Any advice would be appreciated. Xcode 8.3.3, Swift 3, iOS 10.
func saveNewCloudKitRecord() -> Bool {
privateDatabase = container().privateCloudDatabase
recordZone = CKRecordZone(zoneName: "myPatientZone")
var blockSavedToCloudKit = false
let myRecord = CKRecord(recordType: "Patient", zoneID: (recordZone?.zoneID)!)
myRecord.setObject(firstNameTextField.text as CKRecordValue?, forKey: "firstname")
myRecord.setObject(lastNameTextField.text as CKRecordValue?, forKey: "lastname")
let parentRefID = CKRecordID(recordName: "047EBE6C-AB1C-0183-8D80-33C0E4AE228B", zoneID: (recordZone?.zoneID)!)
//
//bunch more record fields
//
let modifyRecordsOperation = CKModifyRecordsOperation(recordsToSave: [myRecord], recordIDsToDelete: nil)
modifyRecordsOperation.timeoutIntervalForRequest = 10
modifyRecordsOperation.timeoutIntervalForResource = 10
modifyRecordsOperation.modifyRecordsCompletionBlock = {
records, recordIDs, error in
if let err = error {
blockSavedToCloudKit = false
//create placeholder record name for later updating
} else {
blockSavedToCloudKit = true
self.currentRecord = myRecord
self.passedInCKRecord = myRecord
}//if err
}//modifyRecordsOperation
privateDatabase?.add(modifyRecordsOperation)
print("blockSavedToCloudKit is \(blockSavedToCloudKit)")
return blockSavedToCloudKit
}//saveNewCloudKitRecord
typealias SavedCompletion = (_ success:Bool) -> Void
func saveTwoFiles(completionHandler : SavedCompletion) {
let flag = saveNewCloudKitRecord()
print("flag is \(flag)")
completionHandler(flag)
print("completionHandler(flag) is \(flag)")
}//makeTheComboSave
func doTheComboSave() {
saveTwoFiles() { (success) -> Void in
print("saveTwoFiles is \(success)")
if success {
//will pass the CKRecord so core data can store the recordID and recordName
saveTheNewRecord()//this is the Core Data save
DispatchQueue.main.async {
self.performSegue(withIdentifier: "unwindToMasterViewController", sender: self)
print("Completion block has been run successfully.")
}//Dispatch
} else {
//create placeholder recordName for later updating
saveTheNewRecord()//this is the Core Data save
DispatchQueue.main.async {
self.performSegue(withIdentifier: "unwindToMasterViewController", sender: self)
print("Completion block has been run but the file save to CloudKit failed.")
}//Dispatch
}//if else
}//block
}//doTheComboSave
Console Output:
blockSavedToCloudKit is false
flag is false
saveTwoFiles is false
completionHandler(flag) is false
Completion block has been run but the file save to CloudKit failed.
success in modifyRecordsOperation
currentRecordName is: B53DCFB8-0EFB-4E79-8762-FECCEFBA9BD8