Search code examples
swiftoperation

completionBlock be called before the operation done


I have some code here:

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
let types: [FeedTimeline] = [.special, .pr, .pickup, .lastPost]
var operations: [Operation] = []
for type in types {
            
    let operation = BlockOperation {
        print("Working")
        self.getArticles(of: type, page: 1, completion: { (articles) in
            print("Fetched")
        })
    }
    operation.completionBlock = {
        print("Done")
    }
    queue.addOperation(operation)

After I ran the code above, I get the result below:

Working

Done

Working

Working

Done

Done

Working

Done

After a while I got 4 times of "Fetched". Why is that? How to make completionBlock only run if the API request (getArticles) done.

What I want is: working -> fetched -> done -> working .... so on


Solution

  • The reason for this behaviour is that the operation continues executing (i.e. exits) once the call to self.getArticles has been executed, not after it has completed.

    One way to fix this is to use dispatch groups. Try the following code:

        let queue = OperationQueue()
        let group = DispatchGroup()
        queue.maxConcurrentOperationCount = 1
        let types: [FeedTimeline] = [.special, .pr, .pickup, .lastPost]
        var operations: [Operation] = []
    
        for type in types {
    
            let operation = BlockOperation {
                group.enter()
                print("Working")
                self.getArticles(of: type, page: 1, completion: { (articles) in
                    print("Fetched")
                    group.leave()
                })
                group.wait(timeout: DispatchTime.distantFuture)
            }
            operation.completionBlock = {
                print("Done")
            }
            queue.addOperation(operation)
    

    In this case the call to group.wait() will block until each call to group.enter() is matched by a call to group.leave(), meaning that the BlockOperation will wait until the call to getArticles has completed.

    Note that you need to ensure that getArticles always executes the callback, if it doesn't (e.g. due to network timeout), the operation block will hang forever.