Prashanth
Prashanth

Reputation: 548

Swift 2 Callback after async execution

I'm trying to get JSON from a service in Swift 2

https://someapidomain.com/entries.json 
[
1212,
1234,
2934,
....
]

https://someapidomain.com/entry/entry_id.json 
{
  "key1": "value1",
  "key2": 23,
  "key3": "value3,
   ...
}

Service must return:
[
 {
  "key1": "value1",
  "key2": 23,
  "key3": "value3,
   ...
},
{
  "key1": "value1",
  "key2": 23,
  "key3": "value3,
   ...
},
....
]


struct ExampleService {

private static let baseURL = "https://someapidomain.com/"
private static let entries_per_page = 10

private static func getJSONResponse(url: String, callback: (AnyObject) -> () ){

        let request = NSMutableURLRequest(URL: NSURL(string: url)!)
        let session = NSURLSession.sharedSession()

        request.HTTPMethod = "GET"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("application/json", forHTTPHeaderField: "Accept")

        let task = session.dataTaskWithRequest(request, completionHandler: {
            data, response, err -> Void in

            do {
                let jsonResponse = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
                callback(jsonResponse)

            }catch {
                print("Error processing json")
            }

        })
        task.resume()


    }

 static func getEntries(url: String, callback: ([[String: AnyObject]]) -> ()) {

        var entries: [[String:AnyObject]] = []
        let apiURL = baseURL + url

        getJSONResponse(apiUrl) { (response) in
            if let entryIds = response as? Array<Int> {
                for entryId in storyIds[0..<entries_per_page] {
                    let entryURL = baseURL + "/entry/\(entryId).json"

                    getJSONResponse(entryURL) { (response) in

                        if let entry = response as? [String: AnyObject] {

                           print(entry)
                           entries.append(entry)

                        }

                    }

                }

               callback(entries)

            }
        }


    }

}


}

Now when I call the service I get empty array

ExampleService.getEntries("/entries.json") { (response) in
     print(response)  // prints []
}

Whereas individual entries inside the loop prints the json. I think its because the callback is getting called before the for loop excecution is finished.

How can I fix this?

Upvotes: 1

Views: 420

Answers (2)

CouchDeveloper
CouchDeveloper

Reputation: 19164

The modified function getEntries sketches how you can accomplish this with a dispatch_group.

Additionally, you MUST ensure your function getJSONResponse always calls the completion handler, even in the case it fails. This is because, you must ensure the function calls dispatch_enter and dispatch_leave are balanced.

I would recommend to modify the getEntries function as well in that sense, that is always call the completion handler even when it fails. Modify the signature of the completion handler accordingly.

static func getEntries(url: String, callback: ([[String: AnyObject]]) -> ()) {

    var entries: [[String:AnyObject]] = []

    getJSONResponse(url) { (response) in
        let queue = dispatch_queue_create("myqueue", nil)
        let group = dispatch_group_create()
        if let entryIds = response as? Array<Int> {
            for entryId in entryIds {
                dispatch_group_enter(group)
                let entryURL = baseURL + "/entry/\(entryId).json"
                getJSONResponse(entryURL) { (response) in
                    if let entry = response as? [String: AnyObject] {
                        print(entry)
                        entries.append(entry)
                    }
                    dispatch_group_leave(group)
                }
            }
        }
        dispatch_group_notify(group, queue) {
            callback(entries)
        }
    }

}

Upvotes: 2

Rashwan L
Rashwan L

Reputation: 38843

Use Grand Central Dispatch

Example below to create, enter and leave

let jsonGroup = dispatch_group_create()

    for x in json{
        dispatch_group_enter(getDeparturesGroup)

        // Some code...
    }

    dispatch_group_wait(jsonGroup, DISPATCH_TIME_FOREVER)

Upvotes: 1

Related Questions