iOS Swift coordinates function returns nil - ios

I'm working on a function to convert a city (string) to coordinates. However, when I call the function I get "(0.0, 0.0)" as a result. It should be the latitude and longitude.
Please help me out. Thanks!
This is the function
func getCoordinates(huidigeLocatie: String) -> (lat: CLLocationDegrees, long: CLLocationDegrees) {
var lat:CLLocationDegrees
var long:CLLocationDegrees
var geocoderHuidigeLocatie = CLGeocoder()
geocoderHuidigeLocatie.geocodeAddressString(huidigeLocatie, completionHandler:
{(placemarks: [AnyObject]!, error: NSError!) in
if error != nil {
println("Geocode failed with error: \(error.localizedDescription)")
} else if placemarks.count > 0 {
let placemark = placemarks[0] as CLPlacemark
let location = placemark.location
var lat = location.coordinate.latitude
var long = location.coordinate.longitude
}
})
return (lat: CLLocationDegrees(), long: CLLocationDegrees())
}

There are two issues here:
You want to return the actual lat and long variables, not CLLocationDegrees().
A more subtle issue is that you're calling a function that returns its results asynchronously, so you cannot return the values immediately. Instead, you might employ your own completionHandler pattern.
For example:
func getCoordinates(huidigeLocatie: String, completionHandler: (lat: CLLocationDegrees!, long: CLLocationDegrees!, error: NSError?) -> ()) -> Void {
var lat:CLLocationDegrees
var long:CLLocationDegrees
var geocoderHuidigeLocatie = CLGeocoder()
geocoderHuidigeLocatie.geocodeAddressString(huidigeLocatie) { (placemarks: [AnyObject]!, error: NSError!) in
if error != nil {
println("Geocode failed with error: \(error.localizedDescription)")
completionHandler(lat: nil, long: nil, error: error)
} else if placemarks.count > 0 {
let placemark = placemarks[0] as CLPlacemark
let location = placemark.location
let lat = location.coordinate.latitude
let long = location.coordinate.longitude
completionHandler(lat: lat, long: long, error: nil)
}
}
}
And you'd call it like so:
getCoordinates(string) { lat, long, error in
if error != nil {
// handle the error here
} else {
// use lat, long here
}
}
// but not here

You should return (lat: lat, long: long).

Related

Domain=kCLErrorDomain Code=8 when fetching location through zipcode

I'm trying to fetch the the latitude and longitude based on the input parameters postal/city and country code. Below is my code, this works fine if enter City and country name but shows error if I enter zipcode and country code. Below is the code. (Note: Location services and app permissions are enabled)
func getLocationFrom(postalCityCode: String, countryCode: String) -> CLLocation? {
let geocoder = CLGeocoder()
var location: CLLocation?
let address = CNMutablePostalAddress()
address.postalCode = postalCityCode
address.country = countryCode
geocoder.geocodePostalAddress(address, preferredLocale: Locale.current) { (placemarks, error) in
guard error == nil else {
print("Error: \(error!)")
return
}
guard let placemark = placemarks?.first else {
print("Error: placemark is nil")
return
}
guard let coordinate = placemark.location?.coordinate else {
print("Error: coordinate is nil")
return
}
location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
print("Found location = \(location)")
}
return location
}
Working input: Shanghai, CN
Failing input: 200040, CN
Edit
Attached updated code as suggested in the answer but still experiencing same issue
Currently, you are using return location before it is set, since geocoder.geocodePostalAddress(...) is an asynchronous function.
That means you need to use a completion handler (for example) to return the location, when it has the results, something like this:
func getLocationFrom(postalCityCode: String, countryCode: String, completion: #escaping ( CLLocation?) -> Void) {
let geocoder = CLGeocoder()
var location: CLLocation?
let address = CNMutablePostalAddress()
address.postalCode = postalCityCode
address.country = countryCode
geocoder.geocodePostalAddress(address, preferredLocale: Locale.current) { (placemarks, error) in
guard error == nil else {
print("Error: \(error!)")
return completion(nil)
}
guard let placemark = placemarks?.first else {
print("Error: placemark is nil")
return completion(nil)
}
guard let coordinate = placemark.location?.coordinate else {
print("Error: coordinate is nil")
return completion(nil)
}
location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
print("\n--> Found location = \(location) \n")
completion(location) // <-- here
}
}
Use it like this:
getLocationFrom(postalCityCode: "200040", countryCode: "CN") { location in
print("\n---> location: \(location) \n")
}
EDIT-1
for testing and isolating the issue, try this code in a new SwiftUI project:
struct ContentView: View {
#State var cityLocation = CLLocation()
var body: some View {
Text(cityLocation.description)
.onAppear {
getLocationFrom(postalCityCode: "200040", countryCode: "CN") { location in
print("\n---> location: \(location) \n")
if let theLocation = location {
cityLocation = theLocation
}
}
}
}
func getLocationFrom(postalCityCode: String, countryCode: String, completion: #escaping ( CLLocation?) -> Void) {
let geocoder = CLGeocoder()
var location: CLLocation?
let address = CNMutablePostalAddress()
address.postalCode = postalCityCode
address.country = countryCode
geocoder.geocodePostalAddress(address, preferredLocale: Locale.current) { (placemarks, error) in
guard error == nil else {
print("Error: \(error!)")
return completion(nil)
}
guard let placemark = placemarks?.first else {
print("Error: placemark is nil")
return completion(nil)
}
guard let coordinate = placemark.location?.coordinate else {
print("Error: coordinate is nil")
return completion(nil)
}
location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
print("\n--> Found location = \(location) \n")
completion(location)
}
}
}

Use variables inside a completion handler somewhere else

I'm trying to convert address to coordinates in order to create a Firestorm GeoPoint object.
I currently have this code:
func getCoords(from address: String, locationCompletionHandler: #escaping (CLLocationCoordinate2D?, Error?) -> Void) {
let geoCoder = CLGeocoder()
geoCoder.geocodeAddressString(address) { (placemarks, error) in
guard
let placemarks = placemarks,
let coordinate = placemarks.first?.location?.coordinate
else {
locationCompletionHandler(nil, error)
return
}
locationCompletionHandler(coordinate, nil)
}
}
func addressToGeoPoint(from address: String) -> GeoPoint {
var latitude:Double = 0
var longitude:Double = 0
getCoords(from: address) { coordinate, error in
if let coordinate = coordinate {
latitude = coordinate.latitude
longitude = coordinate.longitude
}
else {
print("Can't get coords: \(String(describing: error?.localizedDescription))")
}
}
return GeoPoint(latitude: latitude, longitude: longitude)
}
The problem is that when the GeoPoint object is being initialized, the latitude and longitude variables are still 0 because the completion handler hasn't finished yet.
The function addressToGeoPoint must return a GeoPoint.
What can I do in order for this to work?
Thanks!

Convert address to coordinates swift

How can I convert a String address to CLLocation coordinates with Swift?
I have no code yet; I looked for a solution but couldn't find any.
Use CLGeocoder to reverse geocode the address into latitude/longitude coordinates:
let address = "1 Infinite Loop, Cupertino, CA 95014"
let geoCoder = CLGeocoder()
geoCoder.geocodeAddressString(address) { (placemarks, error) in
guard
let placemarks = placemarks,
let location = placemarks.first?.location
else {
// handle no location found
return
}
// Use your location
}
You will also need to add and import CoreLocation framework.
You can use CLGeocoder, you can convert address(string) to coordinate and you vice versa, try this:
import CoreLocation
var geocoder = CLGeocoder()
geocoder.geocodeAddressString("your address") {
placemarks, error in
let placemark = placemarks?.first
let lat = placemark?.location?.coordinate.latitude
let lon = placemark?.location?.coordinate.longitude
print("Lat: \(lat), Lon: \(lon)")
}
Here's what I came up with to return a CLLocationCoordinat2D object:
func getLocation(from address: String, completion: #escaping (_ location: CLLocationCoordinate2D?)-> Void) {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) { (placemarks, error) in
guard let placemarks = placemarks,
let location = placemarks.first?.location?.coordinate else {
completion(nil)
return
}
completion(location)
}
}
So let's say I've got this address:
let address = "Springfield, Illinois"
Usage
getLocation(from: address) { location in
print("Location is", location.debugDescription)
// Location is Optional(__C.CLLocationCoordinate2D(latitude: 39.799372, longitude: -89.644458))
}
Swift 5 and Swift 5.1
import CoreLocation
var geocoder = CLGeocoder()
geocoder.geocodeAddressString("your address") { placemarks, error in
let placemark = placemarks?.first
let lat = placemark?.location?.coordinate.latitude
let lon = placemark?.location?.coordinate.longitude
print("Lat: \(lat), Lon: \(lon)")
}
This works
let geocoder = CLGeocoder()
let address = "8787 Snouffer School Rd, Montgomery Village, MD 20879"
geocoder.geocodeAddressString(address, completionHandler: {(placemarks, error) -> Void in
if((error) != nil){
print("Error", error ?? "")
}
if let placemark = placemarks?.first {
let coordinates:CLLocationCoordinate2D = placemark.location!.coordinate
print("Lat: \(coordinates.latitude) -- Long: \(coordinates.longitude)")
}
})
The CLLocationManager object reports locations as a latitude/longitude pair. While these values uniquely represent any location on the planet, they are not values that users immediately associate with the location. Users are more familiar with names that describe a location, such as street names or city names. The CLGeocoder class lets you convert between geographic coordinates and the user-friendly names associated with that location. You can convert from either a latitude/longitude pair to a user friendly place name, or the other way around.
func getCoordinate( addressString : String,
completionHandler: #escaping(CLLocationCoordinate2D, NSError?) -> Void ) {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(addressString) { (placemarks, error) in
if error == nil {
if let placemark = placemarks?[0] {
let location = placemark.location!
completionHandler(location.coordinate, nil)
return
}
}
completionHandler(kCLLocationCoordinate2DInvalid, error as NSError?)
}
}

How can I plot String Array of addresses on MapView and show markers?

This is for one String whereas I have an array of addresses.
var address = self.address
var geocoder = CLGeocoder()
geocoder.geocodeAddressString(address, completionHandler: {(placemarks: [AnyObject]!, error: NSError!) -> Void in
if let placemark = placemarks?[0] as? CLPlacemark {
self.mapView.addAnnotation(MKPlacemark(placemark: placemark))
}
})
This would do.
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet var mapView: MKMapView!
let addressArray = ["address1", "address2"]
let geocoder = CLGeocoder()
var count = 0
#IBAction func PlotAddresses(sender: AnyObject) {
plotAddressesInMap()
}
func plotAddressesInMap() {
if count < self.addressArray.count {
let address = addressArray[count]
geocoder.geocodeAddressString(address, completionHandler: {(placemarks, error) -> Void in
print("Address = \(address)");
if let placemark = placemarks?.first {
self.mapView.addAnnotation(MKPlacemark(placemark: placemark))
}
print("Count = \(self.count)")
self.count += 1
self.plotAddressesInMap()
})
}
}
}

getting ETA ios - completion handler - swift

I am new to IOS coding and using swift.
I am trying to calculate an ETA between 2 different points. when i convert the address to coordinates, i am storing those in a global var. problem is, since the value is set in a completion handler, when i make the call to calculate the ETA, the vars are not yet set. what would be another way to obtain the same result.
sourceCoords: CLLocationCoordinate2D?
destCoords: CLLocationCoordinate2D?
func getETA()
{
var locationManager:CLLocationManager = CLLocationManager();
locationManager.requestAlwaysAuthorization();
var authorizationStatus:CLAuthorizationStatus = CLLocationManager.authorizationStatus();
if(authorizationStatus == CLAuthorizationStatus.Authorized ||
authorizationStatus == CLAuthorizationStatus.AuthorizedWhenInUse)
{
let geoCoder = CLGeocoder()
let sourceAddress =
[kABPersonAddressStreetKey as NSString: "1 Stockton St",
kABPersonAddressCityKey: "San Francisco",
kABPersonAddressStateKey: "California",
kABPersonAddressZIPKey: "94108"]
//get coordinates
geoCoder.geocodeAddressDictionary(sourceAddress, completionHandler:
{
(placemarks: [AnyObject]!, error: NSError!) in
if error != nil {
println("Geocode failed with error: \(error.localizedDescription)")
} else if placemarks.count > 0 {
let placemark = placemarks[0] as CLPlacemark
let location = placemark.location
self.sourceCoords = location.coordinate
}
});
//var requestSource = MKMapItem.mapItemForCurrentLocation();
let addressDict =
[kABPersonAddressStreetKey as NSString: "2125 Chestnut St",
kABPersonAddressCityKey: "San Francisco",
kABPersonAddressStateKey: "California",
kABPersonAddressZIPKey: "94123"]
//get coordinates
geoCoder.geocodeAddressDictionary(addressDict, completionHandler:
{
(placemarks: [AnyObject]!, error: NSError!) in
if error != nil {
println("Geocode failed with error: \(error.localizedDescription)")
} else if placemarks.count > 0 {
let placemark = placemarks[0] as CLPlacemark
let location = placemark.location
self.destCoords = location.coordinate
}
});
let placeSource = MKPlacemark(coordinate: sourceCoords!,
addressDictionary: sourceAddress)
var requestSource = MKMapItem(placemark: placeSource);
println(placeSource.location.coordinate.latitude)
let place = MKPlacemark(coordinate: destCoords!,
addressDictionary: addressDict)
var requestDestination = MKMapItem(placemark: place);
println(place.location.coordinate.latitude)
var request:MKDirectionsRequest = MKDirectionsRequest();
request.setSource(requestSource);
request.setDestination(requestDestination);
request.transportType = MKDirectionsTransportType.Automobile;
request.requestsAlternateRoutes = false;
var directions:MKDirections = MKDirections(request: request)
directions.calculateDirectionsWithCompletionHandler({
(response: MKDirectionsResponse!, error: NSError?) in
if error != nil{
println("Error")
}
if response != nil{
var mkRoute:MKRoute = response.routes.last as MKRoute;
println(mkRoute.expectedTravelTime)
println(mkRoute.distance)
for step in mkRoute.steps
{
println(step.instructions);
}
}
else{
println("No response")
}
println(error?.description)
})
}

Resources