等待 google 反向地理编码

Wait for google reverse geocoding

我是 swift 的新手。对于我的项目,我需要使用 google 地理编码并将结果放入文本中。对于用户界面,我使用 swiftUI。我尝试对 Completition Handler 做同样的事情,但没有用。下面我用 DispatchQueue 和 DispatchGroup 完成了代码,但是当我尝试使用这个 func 时整个应用程序冻结了。请帮我解决一下这个。 UI 的代码只是调用函数的文本。

func reverseGeocoding(lat: Double, lng: Double) -> String{
    
    var place:String?
    let url = "https://maps.googleapis.com/maps/api/geocode/json?latlng=\(lat),\(lng)&key=KEY"
    let group = DispatchGroup()
    group.enter()
    DispatchQueue.global(qos: .default).async {
        AF.request(url).responseJSON{ response in
            
              //  group.leave()
            guard let data = response.data else {
                return
            }
            do {
                let jsonData = try JSON(data: data)
                let result = jsonData["results"].arrayValue
                
                for result in result {
                    let address_components = result["types"].arrayValue
                    for component in address_components {
                        if(component == "locality"){
                            place = result["formatted_address"].stringValue
                            
                        }
                    }
                }
            } catch let error {
                print(error.localizedDescription)
            }
            
        }
        
    }
    group.wait()
    return place ?? ""
}

你需要一个这样的完成处理程序,它returns还有Result类型

中的所有错误
enum GeoError : Error {
    case locationNoFound
}

func reverseGeocoding(lat: Double, lng: Double, completion: @escaping (Result<String,Error>) -> Void) {
    
    let url = "https://maps.googleapis.com/maps/api/geocode/json?latlng=\(lat),\(lng)&key=KEY"
    DispatchQueue.global(qos: .default).async {
        AF.request(url).responseData { response in
            switch response.result {
                case .success(let data):
                    do {
                        let jsonData = try JSON(data: data)
                        let result = jsonData["results"].arrayValue
                        
                        for result in result {
                            let addressComponents = result["types"].arrayValue
                            for component in addressComponents {
                                if component == "locality" {
                                    completion(.success(result["formatted_address"].stringValue))
                                }
                            }
                        }
                        completion(.failure(GeoError.locationNoFound))
                    } catch {
                        completion(.failure(error))
                    }
                case .failure(let error):
                    completion(.failure(error))
            }
        }
    }
}

并使用它

reverseGeocoding(lat: 45.0, lng: 45.0) { result in
    switch result {
        case .success(let location): print(location)
        case .failure(let error): print(error)
    }
}

继续@vadian 的回答
正如上面提到的发布者取决于上下文。这会给你一个粗略的 idea.This 是从我的理解..


// Replace String with [String] if you want to add multiple locations at once based on it Publisher.send() accepts [String] instead of String
var LocationPublisher = PassthroughSubject<String,Never>()
class Subscriber :ObservableObject {
    @Published var currentLocation :[String] = Array<String>()
    private var cancellebels  = Set< AnyCancellable>()
    func createSubscriber(){
        let subscriber = LocationPublisher.handleEvents(
            receiveSubscription: {subscription in
                print("New subscription \(subscription)")},
            receiveOutput: {output in
                print("New Output  \(output)")
            },
            receiveCancel: {
                print("Subscription Canceled")
            })
            .receive(on: RunLoop.main)
            // if you replace String with [String],TypeOf(value) becomes [String]
            .sink{value in
                print("Subscriber recieved value \(value)")
                self.currentLocation.append(value)
            // use self.currenLocation.append(contentsOf:value) instead
            }

            .store(in: &cancellebels)
        
        
    }
    init() {
       createSubscriber()
    }
}

在这个 contentView 里面

struct ContentView: View {
  @ObservedObject  var locationObject:Subscriber = Subscriber()
    var body: some View {
        VStack{
    
         List{
             locationObject.currentLocation.forEach{ location in
                       Text(location)
             }
         }


        }
    }
}

并从上面的成功完成处理程序中的答案中使用 LocationPublisher.send(location)
而不是打印语句
它将通知订阅者并且 locationObject.currentLocation 将被更新
这只是一种方法,也是最基本的方法。