Asynchronous completion processing in a function with multiple gates / API requests in fast

I was just starting to develop in Swift, so I’m completely new to close. I am also new to handling an asynchronous API request.

I read many similar questions, such as How to get data to return from NSURLSessionDataTask to Swift and How to use Handler close completion with return to Swift? . It helped me, but my problem has changed a bit.

In my function, I want to first make an API request to get the JSON payload. With some data in this JSON payload, I want to make several other API requests. In this case, for each API request, I get a JSON payload where I want to store some data in my own JSON data structure.

The problem is that for every multiple API request, I can only return some of my own JSON data in my CompletionHandler. This is the only way to return data when creating an API request using closure, as I understand it.

So instead of getting multiple completion handlers, when I call my function, I just want to get one.

The thing is, I don’t know how to finish processing several closures in a function, in this case two closures.

I have posted my code below - I have known it for quite some time and may not be so clean. However, the fact is that when im update offers my storeDict, it will be empty, due to dict's suggestions the array gets its information from inside the second closure. This is shown at the bottom of the function.

func getOffersFromWishList(offerWishList: [String], latitude: Double, longitude: Double, radius: Int, completionHandler: ([NSDictionary] -> Void)) { var master: [NSDictionary] = [] var nearby_params: NSDictionary = ["r_lat": latitude, "r_lng": longitude, "r_radius": radius] //println(nearby_params) var store_id_list: [String] = [] // Get all store_ids for store which are nearby (Radius determines how nearby) singleton_eta.api("/v2/stores", type: ETARequestTypeGET, parameters: nearby_params, useCache: true, completion: { (response, error, fromCache) -> Void in if error == nil { let json = JSON(response) storeArray = json.arrayValue //println(storeArray) for store in storeArray { var storeDict = [String: AnyObject]() var metaData = [String: String]() var offers: [NSDictionary] = [] let name = store["branding"]["name"].stringValue let store_id = store["id"].stringValue let street = store["street"].stringValue let city = store["city"].stringValue let zip_code = store["zip_code"].stringValue let dealer_id = store["dealer_id"].stringValue let logo = store["branding"]["logo"].stringValue metaData = ["name": name, "store_id": store_id, "street": street, "city": city, "zip_code": zip_code, "dealer_id": dealer_id, "logo": logo] store_id_list.append(store_id) //println("Butiks ID: \(store_id)") var offset = 0 let limit = 100 // Loop through the offers for the specific store id - only possible to request 100 offers each time // A while loop would be more suitable, but I dont know when to stop, as the length of the offerArray can not be counted as it is cant be accessed outside of the closure. for x in 1...2 { var store_params: NSDictionary = ["store_ids:": store_id, "limit": limit, "offset": offset] println(store_params) // Get offers for a specific store_id singleton_eta.api("/v2/offers", type: ETARequestTypeGET, parameters: store_params, useCache: true, completion: { (response, error, fromCache) -> Void in if error == nil { offerArray = JSON(response).arrayValue //println( "TypeName0 = \(_stdlib_getTypeName(offerArray))") //Loop through the recieved offers for of in offerArray { let name = of["branding"]["name"].stringValue let dealer_id = of["dealer_id"].stringValue let heading = of["heading"].stringValue let description = of["description"].stringValue let price = of["pricing"]["price"].stringValue let image = of["images"]["view"].stringValue //println(heading) // Loop through our offerWishList for owl in offerWishList { let headingContainsWish = (heading.lowercaseString as NSString).containsString(owl.lowercaseString) // Check if offer match with our wish list if(headingContainsWish) { // Save neccesary meta data about each offer to a tuple array var offer = Dictionary<String, String>() offer = ["name": name, "dealer_id": dealer_id, "heading": heading, "description": description, "price": price, "image": image, "offerWishItem": owl] offers.append(offer) } } } } }) //println(storeDict) offset = offset + limit + 1 } storeDict.updateValue(metaData, forKey: "meta_data") storeDict.updateValue(offers, forKey: "offers") // offers is empty due to its appending inside the closure master.append(storeDict) } completionHandler(master) } else { println(error) } }) } 

Call the specified function

 getOffersFromWishList(offerWishList, latitude, longitude, radius) { (master) -> Void in println(master) } 

This is what the wizard will print when the function is called, where the sentences are empty.

 { "meta_data" = { city = "Kongens Lyngby"; "dealer_id" = d8adog; logo = "https://d3ikkoqs9ddhdl.cloudfront.net/img/logo/default/d8adog_3qvn3g8xp.png"; name = "d\U00f8gnNetto"; "store_id" = d2283Zm; street = "Kollegiebakken 7"; "zip_code" = 2800; }; offers = ( ); } { ... } 

So my questions is, what is the correct way to return data from the second closure to the first closure inside the function? Or am I doing this completely wrong? The fact is that I need all this data to view the table and, therefore, I need all the data at once.

+5
source share
1 answer

A few thoughts:

  • If it is possible to return all this with a single request to the server, this can provide better performance. Often the time required to complete requests on the server is inconsequential compared to network latency. If you can avoid having to make one request, get a response, and then issue more requests, that would be ideal.

    Or maybe you request locations at a certain distance in advance, a cache, and then “show me deals for nearby locations” may not require these two sets of requests.

    (I admit that none of them can work for you, but you should consider whether you can do this. If you can eliminate consecutive requests and focus on most concurrent requests, you will get much better performance.)

  • Suppose for a second that the above is not an option, and you are stuck with one request to get nearby locations and another set to get deals. Then you have several options:

    • You can definitely go along the road that you contemplate with a single callback. You can, for example, fulfill all your requests by running dispatch_group_enter before initiating each request, run dispatch_group_leave at the end of each request, and then issue dispatch_group_notify , which will be called when each enter call has been compensated by the corresponding leave call. Thus, create your response object as each request completes, and only when it is done, call completion close.

    • Another approach would be to have a closure that behaves more like an enumeration closure that is called when each site's transactions occur. Thus, the user interface can be updated as it arrives, rather than waiting for everything. If you are on a slow network, updating the user interface as data arrives can be much more acceptable. (For example, consider ten queries, each of which takes 1 second on a slow 3G cellular connection: viewing their pops per second per second is much more tolerable than not having anything for ten seconds).

    • Having said that, you can completely refuse to close. You can consider the delegate - protocol template, in which you specify the delegate for your request, and then implement the protocol methods for each of the responses you receive from the server. This way you can update the interface when new answers appear, instead of holding everything until the last one appears. But we recognize that there are very different types of answers (one of them is a list of sites, the other is a list of deals for this site, the third is “I did everything” and / or “an error occurred”, so when it starts to get complicated, it can It’s better to define a protocol for this interface and handle it this way.

+6
source

All Articles