user172902 user172902 - 2 months ago 18
Swift Question

Function that executes after multiple completion block has finished

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

Answer

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.