0

I'm dealing with some asynchronous functions and trying to update views. In short I have function 1 with asynchronous function that will return a string to be passed to function 2. I am updating views in both functions, on main thread. It all works but I need to understand if this is correct way.

class A {
    var varA = ""
    var varB = ""

    func f1 (_ completion: @escaping (String) -> void ){

        some asynchronous call ... { in
            ... 
            DispatchQueue.main.async {
                self.varA = "something"
                sef.labelA.text = self.varA
                completion(self.varA)
            }
        }
    }

    func f2 (_ string: String){

        another asynchronous call ... { in
            ... 
            DispatchQueue.main.async {
                self.varB = string
                sef.labelB.text = self.varB

            }
        }
    }

    // funcation call
    f1(completion: f2)
}

Three questions, 1) What is the right way to run a dependent function where there is wait for an asynchronous callback?

2) Is DispatchQueue.main.async needed to update views?

3) Is it ok to call async func in another async callback? Isn't there chance self may be nil in some cases if you are updating views in some escaping function?

Jim H.
  • 285
  • 1
  • 2
  • 15

1 Answers1

2

I'm going to try helping you according to your questions:

Question 1) There are many right ways and each developer can have its own logic, but in this case, what I personally would probably do is something like this:

class A {    
    func f1 (_ completion: @escaping (String) -> void ){

        some asynchronous call ... { in
            ... 
            DispatchQueue.main.async { [weak self] in // 1
                guard let strongSelf = self else { return } // 2
                let varA = "something" // 3
                strongSelf.label.text = varA
                completion(varA) // 4
            }
        }
    }

    func f2 (_ string: String){

        another asynchronous call ... { in
            ... 
            DispatchQueue.main.async {
                sef.labelB.text = string // 5
            }
        }
    }

    // function call
    // 6
    f1(completion: { [weak self] text in
        guard let strongSelf = self else { return }
        strongSelf.f2(text)
    })
}

1 - Here I'm using [weak self] to avoid retain cycles.

2 - Just unwrapping the optional self, case it's nil, I'll just return.

3 - In your case, it's not really necessary to have class variables, so I'm just creating local variables inside the block.

4 - Finally, I'm calling the completion with the variable containing the string.

5 - I also don't really need to set a class variable in here, so I'm just updating the label text with the string provided as a paramater.

6 - Then, I just need to call the first function and use the completion block to call the second after the first one completes.

Question 2) Yes, you must call DispatchQueue.main to update the view. This way your making sure that your code will be executed in the main thread that is crucial for things interacting with UI because it allow us to have a sincronization point as you can read in Apple's documentation.

Question 3) Using [weak self] and guard let strongSelf = self else { return }, I'm avoiding retain cycles and the cases where self can be nil.

Alexandre Lara
  • 2,464
  • 1
  • 28
  • 35
  • That explains something. What if I just take out completion: part of func f1 and run f2(varA) separately. How to make sure f2(varA) runs after varA is set? as asynchronous function have delayed returns – Jim H. Aug 04 '17 at 03:39
  • Also is it fine to replace completion(varA) with self.f2(varA), as in just call the function in some asyn closure? – Jim H. Aug 04 '17 at 03:44
  • It's totally fine to replace `completion(varA)`with `self.f2(varA)` so you're also making sure `f2` runs after `f1` completes. There is another way to accomplish that by using `DispatchGroup`, hopefully this answer helps you in case you decide to go this way: https://stackoverflow.com/questions/43689685/observing-asynchronous-requests/43689878#43689878 – Alexandre Lara Aug 04 '17 at 03:50
  • In f1's completion method there is f2 call which also has a completion where views are actually being updated on main thread DispatchQueue.main.async . Does that mean in f1's completion we will do function call in DispatchQueue.main.async { self.f2(varA) } or there is no need for calling that on main thread? – Jim H. Aug 04 '17 at 19:14