Вернуть Bool из CloudKit Сохранить CompletionHandler не правильно

Я создаю приложение, которое сохраняет записи в CloudKit, а также хранит данные локально в Core Data. Я действительно могу сохранять записи в обоих местах, но мне нужно иметь возможность обрабатывать ошибки, когда соединение с iCloud недоступно или возникает ошибка сохранения. Я думал, что могу заполнить переменную в блоке завершения CKModifyRecordsOperation для различения, но это не сработало - возвращаемое значение из сохранения CloudKit всегда ложно, даже если процесс завершился успешно. Идея состоит в том, чтобы сначала сохранить в CloudKit, затем получить идентификатор записи и сохранить его с сохранением Core Data.

Вот мой код, я вызываю doTheComboSave() с помощью кнопки. Вывод консоли находится под кодом. Любой совет будет принят во внимание. 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

Консольный вывод:

blockSavedToCloudKit имеет значение false

флаг ложный

saveTwoFiles является ложным

ЗавершениеHandler (flag) ложно

Блок завершения был запущен, но сохранить файл в CloudKit не удалось.

успех в modifyRecordsOperation

currentRecordName: B53DCFB8-0EFB-4E79-8762-FECCEFBA9BD8


person JohnSF    schedule 05.08.2017    source источник
comment
Я думаю, вы не понимаете асинхронный характер вызова CloudKit / сети. Ложь, возвращенная из saveNewCloudKitRecord, возвращается до завершения операции. Вам понадобится барьер или семафор, чтобы заставить его возвращаться так, как вы ожидаете в своем примере.   -  person agibson007    schedule 05.08.2017


Ответы (1)


См. Комментарий Ичидона выше.

Функция saveNewCloudKitRecord () возвращается до того, как завершениеHandler установит логическое значение, которое я хотел использовать. Вся идея заключалась в том, чтобы сначала создать запись CloudKit, чтобы я мог получить имя записи для хранения в Core Data, чтобы синхронизировать локальные и облачные записи.

Лучше всего сначала сохранить в Core Data, но самому создать CKRecordID и передать его в запись при сохранении в CloudKit.

В функции сохранения записи Core Data:

let myRecordName = UUID().uuidString

Затем верните эту строку, чтобы использовать ее в качестве параметра для сохранения CloudKit.

let myRecordID : CKRecordID = CKRecordID(recordName: myRecordName, zoneID: (recordZone?.zoneID)!)

let myRecord = CKRecord(recordType: "Whatever", recordID : myRecordID)

Надеюсь, это поможет кому-то другому.

person JohnSF    schedule 06.08.2017