1
votes

My app. saves a record to iCloud with CloudKit. I would like to check for errors and set a flag based on success or failure. I return the flag via the function call but that does not work. The return value is false even when the save is a success and there is no error. It's because the saveHistoryRecordToCloud function returns false before the completion handler sets savedToCloud to true. I understand this has to do with the asynchronous network call but I'm very new to this and clearly don't understand how to handle it. How do I set what saveHistoryRecordToCloud returns accurately? Thanks for the help! The code is below.

    func saveHistoryRecordToCloud(currentWalk: MyCustomTabBarController.Walk ) -> Bool
{
    var savedToCloud = false
    let history = CKRecord(recordType: "History")
    history.setValue(currentWalk.startTime, forKey: "startTime")
    history.setValue(currentWalk.duration, forKey: "duration")

    activityIndicator.startAnimating()
    publicDatabase.save(history) { (savedRecord, error) in
        if error != nil
        {
            print ("Error saving to iCloud " + error.debugDescription)
        } else
        {
            savedToCloud = true
            DispatchQueue.main.async {
                self.activityIndicator.stopAnimating
            }
            print ("Saving History worked!! recordID,recordName = ",savedRecord?.recordID.recordName ?? "" )
        }
    }
    return (savedToCloud)
}

and the call

        tbc!.currentWalk.savedToCloud = saveHistoryRecordToCloud(currentWalk: tbc!.currentWalk)
    print ("tbc!.currentWalk.savedToCloud ", tbc!.currentWalk.savedToCloud)  // always returns false
1
Good points: You understood it's async. You just need to add a closure too to saveHistoryRecordToCloud, where you "return" a bool "success" and call the closure in the save(history)() one. - Larme
@Larme How would I do that, please? Thanks! - BfromG

1 Answers

0
votes

You're correct about the calling sequence. One approach is to implement "callback" (or "closure") functions. Assume your current code looks something like this (this is pseudo code, I probably wouldn't get the Swift syntax right):

function1()
{
     line1
     line2
     tbc!.currentWalk.savedToCloud = saveHistoryRecordToCloud(currentWalk: tbc!.currentWalk)
     print ("tbc!.currentWalk.savedToCloud ", tbc!.currentWalk.savedToCloud)  // always returns false
     line 5
     line 6
}

Using a callback function, you'd break that code into two functions like so:

function1()
{
     line1
     line2
     tbc!.currentWalk.savedToCloud = saveHistoryRecordToCloud(currentWalk: tbc!.currentWalk)
}

and

function1_callback(savedToCloud)
{
     print ("tbc!.currentWalk.savedToCloud ", savedToCloud)  
     line 5
     line 6
}

Call function1_callback() from the end of your completion handler, like so:

publicDatabase.save(history) { (savedRecord, error) in
    if error != nil
    {
        print ("Error saving to iCloud " + error.debugDescription)
    } else
    {
        savedToCloud = true
        DispatchQueue.main.async {
            self.activityIndicator.stopAnimating
        }
        print ("Saving History worked!! recordID,recordName = ",savedRecord?.recordID.recordName ?? "" )

        /* new callback added here */
        function1_callback(savedToCloud)
    }
}

The code flow starts with your first function, it calls the saving function, which begins the async saving process and then your code flow ends. When the async save completes, it fires the completion handler. The completion handler calls the callback function, passing in the results of the save operation, and your code flow resumes.