【问题标题】:Wait for google reverse geocoding [duplicate]等待谷歌反向地理编码[重复]
【发布时间】:2021-05-05 18:43:38
【问题描述】:

我是 swift 新手。对于我的项目,我需要使用谷歌地理编码并将结果放入文本中。对于用户界面,我使用的是 swiftUI。我尝试对 Completition Handler 做同样的事情,但没有奏效。下面我用 DispatchQueue 和 DispatchGroup 完成了代码,但是当我尝试使用这个函数时,整个应用程序冻结了。请帮我解决一下这个。 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 ?? ""
}

【问题讨论】:

  • 别等了。滥用DispatchGroup 使网络请求同步也是错误的。完成处理程序是推荐的方式。你有什么问题?
  • 如何使用完成处理程序制作文本视图。我试过了,但没有用。你能告诉我如何使用完成处理程序将结果放入文本视图中
  • 这个 UI 是用 UIKit 还是 SwiftUI 构建的?
  • 它是为 SwiftUI 构建的

标签: swift asynchronous swiftui google-geocoding-api


【解决方案1】:

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


// 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 将被更新
它只是一种方法,也是最基本的方法。

【讨论】:

  • 谢谢你。但是我将如何将它用于文本列表?
  • @KIRCA 我修改了我的答案,我们可以将 currentLocation 设为 Array 并将新的位置或位置组附加到 currentlocation 数组。用于 contentView 中的每个以显示。如果列表很大,您可以使用 Set 丢弃重复项和 LazyVStack ;)
  • 感谢您的回答,这对我很有帮助。 ?
【解决方案2】:

您需要这样的完成处理程序,它还会返回 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)
    }
}

【讨论】:

  • 但是如何使用它在我的 SwiftUI 文件中创建文本视图?
  • 使用@ObservableObject / @Published 模式来通知位置何时可用。
  • 你能给我代码吗?由于我是 Swift 新手,所以我对属性包装器不太了解。这将意味着很多,因为我坚持了 3 天。
  • 这取决于上下文。您的问题不包含任何与 SwiftUI 相关的代码。
  • 我只需要一个调用这个函数的Text视图,结果设置在文本中
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-17
  • 2013-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多