Search code examples
asynchronousswift5completionhandler

perform network call and proceed - asynchronous task


i just started learning Swift a year ago, so please be patient with me :)

i am downloading JSON data with a network call, and as soon as i successfully received those rows, i then continue to clear the rows inside my coreData entity, and rewrite those new rows into coredata..

i am having a hard time understanding this asynchronous procedure..

what i've learned is that i have to use completion handlers, but i still can't use it the way i need to.. especialy when i need to proceed after those 3 steps were executed..

First call from button action:

@IBAction func updateButtonPressed(_ sender: Any) {

    self.myCoreData.update() {(success) in // calls my update method
        print(success!) 
        textField.text = success! // not possible bc not in the Mainthread
}
textField.text = "blabla" // gets executed before the result is available

methods:

func update(completion: @escaping (String?) -> Void) { //parent method which calls sub methods
    var returnValue = ""
    Step1getJson {_ in.      // step 1
        self.Step2Delete {   // step 2
            self.Step3Save { // step 3
                returnValue = "return Value: \(self.step1Result)"
                completion(returnValue)
            }
        }
    }
}

func Step1getJson(completion: @escaping (Bool) -> ()) {
    var success = false
    if let url = URL(string: "https:foo") {
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data else { return }
                do {
                    let parsedJSON = try JSONDecoder().decode([RemoteWire].self, from: data)
                    print("-- Successfully received \(parsedJSON.count) datarows ")
                    self.JSON = parsedJSON
                    self.step1Result = "-- Successfully received \(parsedJSON.count) datarows "
                    success = true

                } catch {
                    print(error)
                }
            completion(success)
        }.resume()
    }
}

func Step2Delete(completion: () -> Void) {
...delete entity rows
completion()
}

func Step3Save(completion: () -> Void) {
.. save new JSON rows to coreData
completion()
}

Everything is working fine that far, and step 2 and step 3 get successfully called when network download has finished..

but how can i proceed after those steps were executed inside my updateButtonPressed function? if i try to write those results into any UI element inside my completion block, a textField or whatever, i get an error that this has to happen in the main thread, and if i execute it outside the completion block those lines get executed far too early, when no results are available yet.

i feel like i have understanding problem with this, i hope you guys can help me out and guide me in the right direction.


Solution

  • As swift allows any changes or updates in UI element only from main thread, you need to call the main thread to update the UI. Replace the below code

    @IBAction func updateButtonPressed(_ sender: Any) {
    
         self.myCoreData.update() {(success) in // calls my update method
            print(success!) 
            textField.text = success! // not possible bc not in the Mainthread
         }
    }
    

    with the new code

    @IBAction func updateButtonPressed(_ sender: Any) {
    
        self.myCoreData.update() {(success) in // calls my update method
            print(success!)
            DispatchQueue.main.async {
                textField.text = success! // Now possible because it is in main thread
            }
        }
        
    }