9

lets propose this scenario

a method with async network operations

func asyncMethodA() -> String?
{
   result : String?
   Alamofire.manager.request(.POST, "https://www.apiweb.com/apimethod", parameters: parameters, encoding:.JSON)
            .response { (request, response, rawdata, error) in
                if (response?.statusCode == 200)
                {
                    //DO SOME HEAVY LIFTING
                }
        }
        return result //string

}

another method with async network operations

func asyncMethodB() -> String?
{
   result : String?
   Alamofire.manager.request(.POST, "https://www.yetanotherapiweb.com/apimethod", parameters: parameters, encoding:.JSON)
            .response { (request, response, rawdata, error) in
                if (response?.statusCode == 200)
                {
                    //DO SOME HEAVY LIFTING

                }
        }
        return result //string
}

a method in which i shall call those methods A and B, to do some operations

func displayResult
{
   1)  let a = asyncMethodA()
   2)  let b = asyncMethodB()
   3)  println(a + b) //some chaotic stuff might happen :(
}

so the question is how i could make that (2) waits for (1) to run, and (3) waits for (2) and so on (that 1,2 and 3 run syncronised)?

(i know that one answer is to chain asyncMethodA and displayResult into asyncMethodB, but want to know if there is some other method)

thank you!.

mike83_dev
  • 481
  • 1
  • 6
  • 11
  • 2
    Are you aware that `return result` in your async functions is executed *before* the data has been retrieved from the network? – Martin R Nov 27 '14 at 19:04
  • I'm not even sure where `result` is declared. – Daniel T. Nov 27 '14 at 19:19
  • yep, i havent seen that too... another problem haha, one possible solution is to use a global variable and to check if has a value different from nil, but if you know an answer for this specific instance of the problem, it would be highly appreciated. – mike83_dev Nov 27 '14 at 19:22

3 Answers3

18
func anAsyncMethod(resultHandler: (result: AnyObject) -> Void) {
    ...        
}

func anotherAsyncMethod(resultHandler: (result: AnyObject) -> Void) {
    ... 
}

let operationQueue = NSOperationQueue()

func performWithCompletionHandler(completion: (AnyObject?, AnyObject?) -> Void) {
        var resultOfOperation1: AnyObject?
        var resultOfOperation2: AnyObject?

        let operation1 = NSBlockOperation {
                let dispatchGroup = dispatch_group_create()
                dispatch_group_enter(dispatchGroup)
                self.anAsyncMethod {
                        result in
                        resultOfOperation1 = result
                        dispatch_group_leave(dispatchGroup)
                }
                // wait until anAsyncMethod is completed
                dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER)
        }

        let operation2 = NSBlockOperation {
                let dispatchGroup = dispatch_group_create()
                dispatch_group_enter(dispatchGroup)
                self.anotherAsyncMethod {
                        result in
                        resultOfOperation2 = result
                        dispatch_group_leave(dispatchGroup)
                }
                // wait until anotherAsyncMethod is completed
                dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER)
        }

        let completionOperation = NSBlockOperation {
                // send all results to completion callback
                completion(resultOfOperation1, resultOfOperation2)
        }

        // configuring interoperation dependencies
        operation2.addDependency(operation1)
        completionOperation.addDependency(operation2)

        operationQueue.addOperations([operation1, operation2, completionOperation], waitUntilFinished: false)
}
linimin
  • 6,239
  • 2
  • 26
  • 31
5

Thanks Yimin for the code above. I've updated it to the latest Swift syntax so just posting to be helpful:

func anAsyncMethod(resultHandler: (_ result: AnyObject) -> Void) {
    ...
}

func anotherAsyncMethod(resultHandler: (_ result: AnyObject) -> Void) {
    ...
}

func performWithCompletionHandler(completion: @escaping (AnyObject?, AnyObject?) -> Void) {

    let operationQueue = OperationQueue()

    var resultOfOperation1: AnyObject?
    var resultOfOperation2: AnyObject?

    let operation1 = BlockOperation {
        let dispatchGroup = DispatchGroup()
        dispatchGroup.enter()
        self.anAsyncMethod {
            result in
            resultOfOperation1 = result
            dispatchGroup.leave()
        }
        // wait until anAsyncMethod is completed
        dispatchGroup.wait(timeout: DispatchTime.distantFuture)
    }

    let operation2 = BlockOperation {
        let dispatchGroup = DispatchGroup()
        dispatchGroup.enter()
        self.anotherAsyncMethod {
            result in
            resultOfOperation2 = result
            dispatchGroup.leave()
        }
        // wait until anotherAsyncMethod is completed
        dispatchGroup.wait(timeout: DispatchTime.distantFuture)
    }

    let completionOperation = BlockOperation {
        // send all results to completion callback
        completion(resultOfOperation1, resultOfOperation2)
    }

    // configuring interoperation dependencies
    operation2.addDependency(operation1)
    completionOperation.addDependency(operation2)

    operationQueue.addOperations([operation1, operation2, completionOperation], waitUntilFinished: false)
}
JaseTheAce
  • 524
  • 5
  • 13
1

With the below, you can launch both async methods at the same time and do your heavy lifting after whichever one finishes last.

var methodAFinished = false
var methodBFinished = false

func asyncMethodA() -> String?
{
    Alamofire.manager.request(.POST, "https://www.apiweb.com/apimethod", parameters: parameters, encoding:.JSON)
        .response { (request, response, rawdata, error) in
            if (response?.statusCode == 200) {
                methodAFinished = true
                doStuff()
            }
        }
    return result //string
}

The guts of asyncMethodB would be methodBFinished = true; doStuff()

func doStuff() {
    if methodAFinished && methodBFinished {
        // do crazy stuff
    }
}
Daniel T.
  • 32,821
  • 6
  • 50
  • 72
  • I thought the idea was that there was something you wanted to do once *both* calls were complete? If you want to do something one just one call is complete, just put the thing in the response block. – Daniel T. Nov 27 '14 at 19:31
  • thx for the answer, mm ok, thats interesting but when doStuff is being called if methodAFinished or methodBFinished are still on the run nothing will happen, i must assure that A and B are finished to do the next operations... i dont know if this might be able to accomplish using GCD or closures... im a newbie in threading :S – mike83_dev Nov 27 '14 at 19:34
  • Right, when both calls are done, both `methodAFinished` and `methodBFinished` will be true, so the crazy stuff inside `doStuff` will happen. – Daniel T. Nov 27 '14 at 19:37