Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS network requests in a serial queue

In my View Controller's I make network calls using a class called Client. Client is basically a wrapper around Alamofire (AFNetworking) network calls, so I have save (POST), get (GET), and delete (DELETE) methods.

Each view controller has an instance variable client, and user's can Create, Edit, and Delete objects like so:

client.save(object) { error in
     print(error)
}
client.delete(object)

I want to ensure that for a given client, the same object isn't being simultaneously deleted/modified. There may be multiple instances of Client dealing with the same user-editable objects.

My idea is to create a serial queue that all instances of a client will use to enqueue requests, however I'm running into issues with the closure's of each function. I want the requests to be done asynchronously (to avoid blocking main thread) and Alamofire uses closures, but I want the Client to treat the requests as synchronous so that they are performed in the order received and wait for all closures to complete before proceeding to the next request to avoid any conflicts.

My Client save method looks like this:

dispatch_async(self.serialQueue) {
    self.httpClient.post("url", object) { error in
         // handle errors, maybe make another request, etc
    }
}

How would I go about ensuring each request (and its closures) complete before the client executes the next request. I need to be able to support chained requests as well (so in a closure I'd make another request).

like image 710
narciero Avatar asked Jan 22 '16 18:01

narciero


1 Answers

This can be done easily with a semaphore

All you have to do is let the semaphore get captured into the closure, and signal it once you're done with a given network task.

let semaphore = dispatch_semaphore_create(1);

for i in 0...10 {

    dispatch_async(serialQueue, {

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) // wait for the previous network task to complete. you could also setup a timeout here.

        self.doNetworkTask({error in

            // do error handling & make new requests

            dispatch_semaphore_signal(semaphore) // once done, signal the semaphore to start next task.
            print("network task \(i) done")
        })

    })

}

I hope this helps you!

like image 143
Hamish Avatar answered Oct 19 '22 21:10

Hamish