Search code examples
iosswiftnsoperationnsoperationqueue

NSOperationQueue finishes all tasks swift 3


I am trying to achieve NSOperationQueue finishing all tasks operation in swift 3. I create a below demo code and it is working according to my expectation.

func downloadTopStroiesDetails(){
    let operationQueue: OperationQueue = OperationQueue()
    let operation1 = BlockOperation() {
         print("BlockOperation1")
        for id in 0...5{
            operationQueue.addOperation(downloadArticle(index: id))
        }
        let operation2 = BlockOperation() {
            print("BlockOperation2")
        }
        operationQueue.addOperation(operation2)
    }
    operationQueue.addOperation(operation1)
}

func downloadArticle(index:Int) -> Operation {
    let operation: Operation = BlockOperation { () -> Void in
        print(index)
    }
    return operation
}
downloadTopStroiesDetails() // start calling 

Output :

BlockOperation1
0
1
2
3
4
5
BlockOperation2

But when I call a Web API with Alamofire in downloadArticle method output is different.

func downloadArticle(index:Int) -> Operation {
        let operation = BlockOperation(block: {
            RequestManager.networkManager.fetchFromNetworkwithID(articleid: index) { (response:Any ,sucess:Bool) in
                if sucess{
                      print(index)
                    //let art = article.init(json:(response as? json)!)!
                    // self.saveDataIntoCoreData(data: art)
                    //self.all_TopArticle.append(art)
                }
            };
        })
        return operation
    }

Now output :

BlockOperation1
BlockOperation2
0
1
2
3
4
5

What i am doing wrong here ?


Solution

  • Your downloadArticle method is creating a block operation that completes immediately because it in turn performs an asynchronous operation.

    You need to prevent the block from reaching the end until the async fetch completes. Using a semaphore would be one solution.

    func downloadArticle(index:Int) -> Operation {
        let operation = BlockOperation(block: {
            let semaphore = DispatchSemaphore(value: 0)
            RequestManager.networkManager.fetchFromNetworkwithID(articleid: index) { (response:Any ,sucess:Bool) in
                if sucess{
                      print(index)
                    //let art = article.init(json:(response as? json)!)!
                    // self.saveDataIntoCoreData(data: art)
                    //self.all_TopArticle.append(art)
                }
                semaphore.signal()
            };
            semaphore.wait()
        })
        return operation
    }
    

    The use of this semaphore ensure the operation doesn't actually complete until the network fetch is also complete.

    You might also want to make your operation queue serial instead of concurrent to ensure you only allow one operation to run at a time. If this is what you want, then set the operation queue's maxConcurrentOperationCount to 1.