2

I have a funciton that I would like to only execute IF the two completion block has completed (And no way to tell which one to finish first). Below is my attempt that works. However, it is very messy and if I there are three or more completion block that I want to wait for, I would have flags everywhere. I was wondering if there is a prettier way of doing it.

class TestClass: UIViewController {
    var blockOneComplete = false
    var blockTwoComplete = false

    func blockOneDownloadImageDescription(completion:()->Void) {
        downloadAsyncWithCompletion {
            blockOneComplete = true 
            if self.blockTwoComplete == true {
                self.allDataDownloadCompleted()
            } else {
                // Do nothing and wait for block Two to complete
            }
        }
    }

    func blockTwoDownloadImageData(completion:()->Void) {
        downloadAsyncWithCompletion {
            blockTwoComplete = true 
            if self.blockOneComplete == true {
                self.allDataDownloadCompleted()
            } else {
                // Do nothing and wait for block One to complete
            }
        }
    }

    func allDataDownloadComplete() {
        // Execute this funciton after all Async Download has complete
    }
}

-- Update with final result -- Turns out that what was outlined in this website was exactly what I needed Using dispatch groups to wait for multiple web services

I believe this was not a duplicate of the SO question mentioned in the comment because the final solution included dispatch_group_enter and dispatch_group_leave

user172902
  • 3,541
  • 9
  • 32
  • 75
  • 2
    Possible duplicate of [How do I know all my tasks in Grand Central Dispatch finished?](http://stackoverflow.com/questions/9632235/how-do-i-know-all-my-tasks-in-grand-central-dispatch-finished) – Tj3n Sep 20 '16 at 08:06
  • I am not sure if this is duplicate as that example uses GCD and my download code does not use it (from my level anyway). It The download code is standard firebase query block with call back. So I am not sure how to properly integrate that in my case. – user172902 Sep 20 '16 at 08:52
  • I would use NSNotificationCenter and call `allDataDownloadComplete()` for each one... Then in `allDataDownloadComplete` just check that all the flags are true before it's allowed to execute. I haven't found another way to do it - flags aren't too bad either - they tend to come in handy later again :) – Byron Coetsee Sep 20 '16 at 09:06
  • if it's async, then you can use dispatch_group, it doesnt matter, just need place the group exit at the completion – Tj3n Sep 20 '16 at 09:23

2 Answers2

4

The best option is by using dispatch_group

class TestClass: UIViewController {

    var group : dispatch_group_t = dispatch_group_create()

    override func viewDidLoad() {
        super.viewDidLoad()
        dispatch_group_notify(group, dispatch_get_main_queue()) { 
            allDataDownloadComplete()
        }
    }

    func blockOneDownloadImageDescription(completion:()->Void) {
        dispatch_group_enter(group)
        downloadAsyncWithCompletion {
            dispatch_group_leave(group)
        }
    }

    func blockTwoDownloadImageData(completion:()->Void) {
        dispatch_group_enter(group)
        downloadAsyncWithCompletion {
            dispatch_group_leave(group)
        }
    }

    func allDataDownloadComplete() {
        // Execute this funciton after all Async Download has complete
    }
}
iCoder86
  • 1,874
  • 6
  • 26
  • 46
Patrick R
  • 6,621
  • 1
  • 24
  • 27
3

You will need either use dispatch_group or use functional reactive programming library like RxSwift to achieve it if you does not want to manage flags.

However, you can just use one counter flag and just make a function call or use NSNotification if is for another ViewController.

In one of my project, I need to ensure that at least 3 of the 4 completion block is completed before calling some function. I do it something like this:

class TestClass: UIViewController {
    var numberOfBlockCompleted = 0

    func blockOneDownloadImageDescription(completion:()->Void) {
        downloadAsyncWithCompletion {
            numberOfBlockCompleted += 1 
            self.allDataDownloadCompleted()
        }
    }

    func blockTwoDownloadImageData(completion:()->Void) {
        downloadAsyncWithCompletion {
            numberOfBlockCompleted += 1 
            self.allDataDownloadCompleted()
        }
    }

    func blockThreeDownloadImageDesc(completion:()->Void) {
        downloadAsyncWithCompletion {
            numberOfBlockCompleted += 1
            self.allDataDownloadCompleted()
        }
    }


    func allDataDownloadComplete() {
        if numberOfBlockCompleted == 3 {
            //do something
        }
    }
}

In my opinion, it depend largely on how complex is the app. If is just for one or two part, a flag is good enough. However, if the app depending largely on chaining network calls and fetching from different server that need to wait for one or another to be completed like a live stocks app then a strong knowledge of GCD or using functional reactive programming will make your job easier in the long run.

Zac Kwan
  • 5,587
  • 4
  • 20
  • 27