我有一个for循环,其结果从searchRequest到Google递增。我使用DispatchGroup来确保在更新UI之前从searchRequest获取所有数据。但是,没有调用我的dispatchGroup.notify函数,因此从未更新过我的UI。请参见下面的代码:
func donationCenters(completion: @escaping ([DonationCenter])->()) {
var placeSearchQuery = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(String(self.coordinates.latitude)),\(String(self.coordinates.longitude))&radius=1500&keyword=donation center&key=###########"
// GROUP CREATED
let myGroup = DispatchGroup()
placeSearchQuery = placeSearchQuery.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
var urlRequest = URLRequest(url: URL(string: placeSearchQuery)!)
urlRequest.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: urlRequest) { (data, resopnse, error) in
if error == nil {
let jsonDict = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
if let dict = jsonDict as? Dictionary<String, AnyObject> {
if let results = dict["results"] as? [Dictionary<String, AnyObject>] {
var donationCenters: [DonationCenter] = []
for result in results {
myGroup.enter() // ENTER GROUP
let donationCenter = DonationCenter(name: text, image: image, latitude: location["lat"] as! Double, longitude: location["lng"] as! Double, phone: formattedPhoneNumber, website: website)
donationCenters.append(donationCenter)
myGroup.leave() // LEAVE GROUP
}
// NOTIFY - NEVER CALLED (done never printed)
myGroup.notify(queue: .main) {
print("done")
completion(donationCenters)
}
}
}
} else {
// Error with search request
}
}
task.resume()
}
更新的代码:
func donationCenters(completion: @escaping ([DonationCenter])->()) {
if let coordinates = self.coordinates {
var placeSearchQuery = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(String(coordinates.latitude)),\(String(coordinates.longitude))&radius=1500&keyword=donation center&key=###########"
placeSearchQuery = placeSearchQuery.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
var urlRequest = URLRequest(url: URL(string: placeSearchQuery)!)
urlRequest.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: urlRequest) { (data, resopnse, error) in
if error == nil {
let jsonDict = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
if let dict = jsonDict as? Dictionary<String, AnyObject> {
if let results = dict["results"] as? [Dictionary<String, AnyObject>] {
var donationCenters: [DonationCenter] = []
for result in results {
let text = result["name"] as! String
if let images = result["photos"] as? [Dictionary<String, AnyObject>] {
if let photoReference = images[0]["photo_reference"] as? String {
self.imageForPhotoReference(photoReference, completion: { image in
if let image = image {
if let placeId = result["place_id"] as? String {
var placeDetailsQuery = "https://maps.googleapis.com/maps/api/place/details/json?placeid=\(placeId)&fields=name,formatted_phone_number,website,formatted_address,geometry&key=########"
placeDetailsQuery = placeDetailsQuery.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
var urlRequest = URLRequest(url: URL(string: placeDetailsQuery)!)
urlRequest.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: urlRequest) { (data, resopnse, error) in
if error == nil {
let jsonDict = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
if let dict = jsonDict as? Dictionary<String, AnyObject> {
if let result = dict["result"] as? Dictionary<String, AnyObject> {
if let formattedPhoneNumber = result["formatted_phone_number"] as? String {
if let website = result["website"] as? String {
if let geometry = result["geometry"] as? Dictionary<String, AnyObject> {
if let location = geometry["location"] as? Dictionary<String, AnyObject> {
let donationCenter = DonationCenter(name: text, image: image, latitude: location["lat"] as! Double, longitude: location["lng"] as! Double, phone: formattedPhoneNumber, website: website)
donationCenters.append(donationCenter)
}
}
}
}
}
}
} else {
//we have error connection google api
}
}
task.resume()
}
}
})
// IF EMPTY RETURN NO REGISTERED DONATION CENTERS IN YOUR AREA
}
}
}
print("done")
completion(donationCenters)
}
}
} else {
// Error with search request
}
}
task.resume()
}
}
最佳答案
为了能够理解它的工作原理和用法,请先尝试简化代码,然后再使用所有功能进行扩展。
让我们看一下这段代码
for result in results {
myGroup.enter() // ENTER GROUP
let donationCenter = DonationCenter(name: text, image: image, latitude: location["lat"] as! Double, longitude: location["lng"] as! Double, phone: formattedPhoneNumber, website: website)
donationCenters.append(donationCenter)
myGroup.leave() // LEAVE GROUP
}
因为那里只有同步代码,所以它等效于
for result in results {
myGroup.enter()
myGroup.leave()
}
最后
for result in results {}
如您所见,您的小组那里没有任何功能!
打开您的游乐场,并尝试了解如何使用DispatchGroup进行要求(在完成所有背景任务后通知您)
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
// it mimics some function which do some job in the background
// as dataTask in your example
func asyncFoo(id: Int, completition: @escaping (_ result: String)->()) {
let q = DispatchQueue(label: "internal", qos: .background, attributes: .concurrent)
q.async {
// running in the backgroud
var sum = 0
for i in 1...1000 {
let r = Int.random(in: 0..<i)
sum += r
}
let res = sum.description
completition(res)
}
}
let group = DispatchGroup()
for i in 0..<10 {
group.enter() // enter the group before the task starts
asyncFoo(id: i) { (result) in
print("id:", i, result)
group.leave() // leave the group when task finished
}
}
group.notify(queue: .main) {
print("all done")
PlaygroundPage.current.finishExecution()
}
print("continue execution ...")
它打印出类似
continue execution ...
id: 4 260320
id: 2 252045
id: 8 249323
id: 3 265640
id: 0 256478
id: 1 253038
id: 5 252521
id: 9 255435
id: 6 245125
id: 7 252262
all done