我刚刚开始在Swift开发,所以我对闭包很新 . 我也是新手如何处理异步API请求 .
我已经阅读了很多类似的问题,例如How to get data to return from NSURLSessionDataTask in Swift和How to use completionHandler Closure with return in Swift? . 这些帮助了我,但我的问题有点不同 .
在我的函数中,我想首先发出一个API请求来获取JSON有效负载 . 对于此JSON有效内容中的一些数据,我想要进行多个其他API请求 . 在这种情况下,我将为每个API请求接收一个JSON有效负载,其中我想将一些数据存储在我自己的JSON数据结构中 .
问题在于,对于我生成的每个多个API请求,我只能在CompletionHandler中返回我自己的JSON数据的一部分 - 据我所知,这是在使用闭包发出API请求时返回数据的唯一方法 .
因此,在调用我的函数时,我只想获得一个单独的函数,而不是获得多个完成处理程序 .
事情是我不知道如何完成处理函数中的几个闭包,在这种情况下是两个闭包 .
我在下面发布了我的代码 - 我知道它很长,也许不那么干净 . 但是,关键是当我更新我的storeDict提供时,这将是空的,因为提供dict数组从第二个闭包内部获取其信息 . 这显示在函数的底部 .
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)
}
})
}
调用上面的功能
getOffersFromWishList(offerWishList, latitude, longitude, radius) { (master) -> Void in
println(master)
}
这是主人在调用函数时打印的内容,其中商品是空的 .
{
"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 = (
);
}
{
...
}
所以我的问题是,将数据从第二个闭包返回到函数内的第一个闭包的正确方法是什么?或者我是以完全错误的方式做到这一点的?问题是,我需要所有这些数据用于tableview,因此需要一次性所有数据 .
1 回答
几点想法:
或者您可能提前在一定距离内请求位置,缓存该位置,然后“向我显示附近位置的交易”可能不需要这两组请求 .
(我知道这些都不适合你,但是如果可以的话就要考虑 . 如果你可以消除连续的请求并专注于大部分并发的请求,那么你的性能会更好 . )
让我们假设一秒钟以上不是一个选项,并且你被困在一个请求以获得附近的位置和另一个集合来获得交易 . 然后你有几个选择:
你肯定可以通过一次回调继续前进 . 例如,您可以发出所有请求,在发出每个请求之前执行
dispatch_group_enter
,在每个请求完成后执行dispatch_group_leave
,然后发出dispatch_group_notify
,当每个enter
调用已被相应的偏移时,将调用dispatch_group_notify
leave
来电 . 因此,在每个请求完成时构建您的响应对象,并且只有在它们完成时,才调用完成闭包 .另一种方法是使一个闭包行为更像一个枚举闭包,一个在每个站点的交易进入时调用 . 这样,UI可以随着事物的进入而更新,而不是等待一切 . 如果您的网络速度很慢,那么在数据输入时更新UI可能会更容易接受 . (例如,考虑十个请求,每个请求在缓慢的3G蜂窝连接上完成1秒钟:观看它们每秒一次弹出比在十秒内看不到任何东西要容易得多) .
话虽如此,你可能想彻底放弃关闭 . 您可以考虑
delegate
-protocol
模式,在其中为您的请求指定delegate
,然后为从服务器获得的每个响应实现protocol
方法 . 这样你可以在新响应进入时更新UI,而不是在最后一个响应进入之前保留所有内容 . 但我们认识到有非常不同类型的响应(一个是站点列表,另一个是列表处理给定的站点,第三个是"I'm all done"和/或“有一个错误”,所以当它开始变得复杂时,为这个接口定义一个协议可能会更好,并以这种方式处理它 .