Allocating the results of Reverse Geocoding to a global variable

467 Views Asked by At

I am using Swift 4 and Xcode 9.3.1. I'm including screenshots of the code.

I am new to mobile development/ programming in general and have been thinking about how to phrase this. So this is the best I can explain it:

I am building an app that gets the user's location in order to send assistance through to them. The app gets the user's coordinates, and displays a TextView with their address information. So pretty straight forward mapKit/coreLocation functionality. So far, so good: Getting the coordinates with startUpdatingLocation() works fine, and I've used Reverse Geocoder to get the street name & locality. But they-- meaning the decoded street and locality strings-- only print out if I call them within the closure, not outside it. I've understood (correctly or incorrectly?) that variables that need to be available for multiple functions within a class should to be declared globally at the top. However I can't figure out how to extract the information from this closure in order to use it elsewhere.

I've been googling and reading through questions in stackOverflow and I feel like something really simple is missing but can't figure out what. Things I've tried unsuccessfully so far:

1_ Defining global variables as empty strings at the beginning of the class and using the variable names inside the closure where the geocoding reverse method happens, in an attempt to store the resulting strings, but when I try to print the variables outside the closure, the global variable is still and empty string ("").

[global variables declared at the top][1]

2_Defining an empty, global array of strings and appending the information from inside the closure to the global array. Still got an empty array outside the closure. (so same as 1)

3_Create a function --func decodedString()-- to return the data as a String, so I can use it by declaring

*let streetLocation : String = decodedString()*

However when I declare that function like this :

var street = ""
var locality = ""

// Get the street address from the coordinates
    func deocodedString() -> String {
        let geocoder = CLGeocoder()
        geocoder.reverseGeocodeLocation(location) { placemarks, error in
        if let placemark = placemarks?.first {
        self.street = placemark.name!
        self.locality = placemark.locality!
        let string = "\(self.street), \(self.locality)"
        return string
        }
    }
}

I get an error of: Unexpected non-void return value in void function

unexpected no void return value in void function

Lastly, if I pass the information straight into a TextView within the closure by using the code below, my textView updates successfully-- but I can't format the strings, which I need to do in order to make them look like the design instructions I'm following (aka some bold text, some regular text, and some different sizes of text):

CLGeocoder().reverseGeocodeLocation(location) { placemarks, error in
    if let placemark = placemarks?.first {
        self.street = placemark.name!
        self.locality = placemark.locality!
        let string = "\(self.street), \(self.locality)"
        self.addressTextView.text = string
    }
}

So that's why I can't just pass it through with the textView.text = string approach.

I'd appreciate some help...I have been looking though StackOverFlow, youtube and other tutorial places but I can't figure out what I'm missing, or why my function declaration generates an error. I have already destroyed and reversed my code several times over last 24 hs without getting an independent string that I can apply formatting to before passing it into the textView and I'm at a loss as to how else to approach it.

2

There are 2 best solutions below

3
On BEST ANSWER

When you call this function the reverseGeocodeLocation runs in the background thread. So if you want to return the address in this method you should use escaping closure.

    func getaddress(_ position:CLLocationCoordinate2D,completion:@escaping (String)->()) {
     let geocoder = CLGeocoder()
        geocoder.reverseGeocodeLocation(location) { placemarks, error in
        if let placemark = placemarks?.first {
        let street = placemark.name!
        let locality = placemark.locality!
        let string = "\(street), \(locality)"
        completion(string)
        }
    } 
    }

   self.getaddress(position.target) { address in
        print(address)
        self.addressTextView.text = address    
   }
0
On

I had a problem with google geocoder to update the label on the map screen.

So I did this, first, create

swift file name: GoogleAPI just call it as you like.

class GoogleAPI {

    static let sharedInstance = GoogleAPI()

    private init() { }

    var addressValue = ""

    public func geocoding(lat: Double, long: Double) {
        Alamofire.request("https://maps.googleapis.com/maps/api/geocode/json?latlng=\(lat),\(long)&key=YOUR_GOOGLE_KEY").responseJSON { (response) in
            if response.result.isSuccess {
                let dataJSON : JSON = JSON(response.result.value!)
                self.geocoding(json: dataJSON)
            } else {
                print("Error \(response.result.error!)")
            }
        }
    }

    fileprivate func geocoding(json: JSON) {
        let json = json["results"]
        let address = json[1]["formatted_address"].stringValue
        addressValue = address
        print("pin address \(addressValue)")
    }
}

This is an API call to Google to fetch all from a response and parse the only street.

After that go to your View Controller with a map where is the pin, map etc..

Set up a pin, marker to be draggable. [marker1.isDraggable = true]

Then add this function

mapView(_ mapView: GMSMapView, didEndDragging marker: GMSMarker)

and add call from above like this :

func mapView(_ mapView: GMSMapView, didEndDragging marker: GMSMarker) {
    GoogleAPI.sharedInstance.geocoding(lat: marker.position.latitude, long: marker.position.longitude)
    DispatchQueue.main.async {
        self.txtSearch.text = GoogleAPI.sharedInstance.addressValue
    }
}

txtSearch is my search text field.

yea I know, that can be done better, but no time. this is working.

Swift 4.2