2017-08-05 8 views
0

Je crée une application qui enregistre des enregistrements dans CloudKit et stocke également les données localement dans les données de base. Je peux en effet enregistrer des enregistrements dans les deux emplacements, mais je dois être capable de gérer les erreurs lorsqu'une connexion à iCloud n'est pas disponible ou qu'il y a une erreur de sauvegarde. J'ai pensé que je pourrais remplir une variable dans le bloc d'achèvement CKModifyRecordsOperation pour différencier, mais cela ne fonctionne pas - la valeur de retour de la sauvegarde CloudKit est toujours fausse, même si le processus réussit. L'idée est d'enregistrer d'abord dans CloudKit, puis de récupérer l'ID d'enregistrement et de l'enregistrer avec l'enregistrement des données de base.Renvoyer Bool à partir de CloudKit Enregistrer CompletionHandler Not Correct

Voici mon code, j'appelle doTheComboSave() à partir d'un barbutton. La sortie de la console est en dessous du code. Tout avis sera le bienvenu. 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 

Sortie de la console:

blockSavedToCloudKit est fausse

drapeau est faux

saveTwoFiles est fausse

completionHandler (drapeau) est faux

Le bloc d'achèvement a été exécuté mais le fichier enregistré dans CloudKit a échoué.

succès dans modifyRecordsOperation

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

+0

Je pense que vous ne comprenez pas la nature asynchrone d'un appel CloudKit/réseau. Le faux retourné par saveNewCloudKitRecord est renvoyé avant que l'opération ne soit terminée. Vous auriez besoin d'une barrière ou d'un sémaphore pour le faire revenir comme vous l'attendez dans votre exemple. – agibson007

Répondre

0

Voir le commentaire de Ichydon ci-dessus.

La fonction saveNewCloudKitRecord() retourne avant l'achèvementHandler définit la valeur booléenne que je voulais utiliser. L'idée était de créer l'enregistrement CloudKit en premier afin que je puisse récupérer le recordName à stocker dans les données de base afin de synchroniser les enregistrements locaux et en nuage.

Une meilleure approche consiste à enregistrer d'abord les données de base, mais à créer moi-même le CKRecordID et à l'alimenter à l'enregistrement lors de l'enregistrement dans CloudKit.

Dans la fonction pour sauvegarder l'enregistrement des données de base:

let myRecordName = UUID().uuidString 

Retournez ensuite cette chaîne à utiliser comme paramètre pour la CloudKit sauver.

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

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

Espérons que cela aide quelqu'un d'autre.