swiftasynchronousswiftuigoogle-geocoding-api

Wait for google reverse geocoding


I am new with swift. For my project I need to use google geocoding and to put the result in a text. For the user interface I am using swiftUI. I tried to do the same with Completition Handler but that didn't work. Below I have the code done with DispatchQueue and DispatchGroup but the whole application freezes when I try to use this func. Please help me with this. The code for UI is just a Text calling the func.

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 ?? ""
}

Solution

  • continuation for @vadian answer
    As mentioned in the above publisher depends on context .This will give you a rough idea.This is from what I understood..

    
    // 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()
        }
    }
    

    And inside this contentView

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

    and from the above answer inside the success completion handler use LocationPublisher.send(location)
    instead of print statement
    it will be notified to subscribers and locationObject.currentLocation will be updated
    Its just one way to do it and most basic way.