Pangu Pangu - 6 months ago 26
Swift Question

How to make inner async request complete first before completing outer async request in Swift?

I've been trying to achieve this for some time and cannot get it to work.

First let me show a simple sample code:

override func viewDidLoad()
{
super.viewDidLoad()

methodOne("some url bring")
}

func methodOne(urlString1: String)
{
let targetURL = NSURL(string: urlString1)

let task = NSURLSession.sharedSession().dataTaskWithURL(targetURL!) {(data, response, error) in

// DO STUFF
j = some value

print("Inside Async1")
for k in j...someArray.count - 1
{
print("k = \(k)")
print("Calling Async2")
self.methodTwo("some url string")
}

}

task.resume()
}

func methodTwo(urlString2: String)
{
let targetURL = NSURL(string: urlString2)

let task = NSURLSession.sharedSession().dataTaskWithURL(targetURL!) {(data, response, error) in

// DO STUFF
print("inside Async2")
}

task.resume()
}


What I'm basically doing is I perform an asynchronous request within my
methodOne
, and within that function, I call my
methodTwo
which performs another asynchronous request.

The problem I'm having is that when
methodTwo
is called, it never enters the asynchronous session. It does however, within
methodTwo
, enter the asynchronous session, but only once
k = someArray.count - 1
. It's basically queuing up until the very end, which is not what I'd like to achieve.

Here's a sample output:

Inside Async1
k = 0
Calling Async2
Inside Async1
k = 0
Calling Async2
k = 1
Calling Async2
Inside Async1
k = 0
Calling Async2
k = 1
Calling Async2
k = 2
Calling Async2
Inside Async1
.....
Inside Async1
k = 0
Calling Async2
k = 1
Calling Async2
k = 2
Calling Async2
k = 3
Calling Async2
k = 4
Inside Async2


In other words, I'd like to have the async request from
methodTwo
complete on each iteration before the async request from
methodOne
completes.

Here's a sample output of what my goal is:

Inside Async1
k = 0
Calling Async2
Inside Async2
Inside Async1
k = 1
Calling Async2
Inside Async2
Inside Async1
...


I've found something similar here: Wait until first async function is completed then execute the second async function

However, I couldn't get this to work with the suggestions and solutions.

Can someone point me in the right direction?

Thanks

Answer

One way to do this is to change methodTwo() to accept a callback as an argument, then you can use a semaphore:

func methodOne(urlString1: String) {
    let targetURL = NSURL(string: urlString1)
    let task = NSURLSession.sharedSession().dataTaskWithURL(targetURL!) { data, response, error in
        let queue = dispatch_queue_create("org.myorg.myqueue", nil)
        dispatch_async(queue) {

            // DO STUFF
            j = some value

            print("Inside Async1")
            for k in j...someArray.count - 1 {  
                print("k = \(k)")

                print("Calling Async2")
                dispatch_semaphore_t sem = dispatch_semaphore_create(0);
                self.methodTwo("some url string") {
                    dispatch_semaphore_signal(sem);
                }
                dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
            }
        }
    }
    task.resume()
}

func methodTwo(urlString2: String, callback: (() -> ())) {
    let targetURL = NSURL(string: urlString2)
    let task = NSURLSession.sharedSession().dataTaskWithURL(targetURL!) { data, response, error in

        // DO STUFF
        print("inside Async2")
        callback()
    }
    task.resume()
}

Note that to not block the delegate queue of methodOne's task callback, the example creates its own queue that you can block at will.