Cannot save the longitute and latitude from CLGeocoder() - ios

I want to retrieve the longitute and latitude using an address as a string. I found this very useful post here: Convert address to coordinates swift
But when I want to save the results in a double field and return it I can't. What I have done is
func getLatitude(address:String) -> Double{
var lati = 0.0
var geocoder = CLGeocoder()
geocoder.geocodeAddressString("your address") {
placemarks, error in
let placemark = placemarks?.first
if let lat = placemark?.location?.coordinate.latitude{
lati = lat
}
}
}
return lati
}
Inside the geocoder.geocodeAddressString block the value is populated but when I try to return it always gives me 0.0 and I have tried everything. Any ideas please?
If it try to print the value inside the inner block of code it gets printed but I can never return it.
Thank you in advance for the answers.

CLLocationCoordinate2D is struct of latitude and longitude both defined as CLLocationDegrees which itself is a typealias of Double.
var latitude: Double?
var longitude: Double?
func getLocation(address: String) {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) { placemarks, error in
guard let placemark = placemarks?.first else { return }
let coordinate = placemark.location?.coordinate
latitude = coordinate?.latitude
longitude = coordinate?.longitude
}
}

Related

Access to the coordinates from an address swift

Hi how can I access to the longitude and latitude parameters outside of this block ?
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(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)")
}
I want to save this two params as Float or String in firebase. Thanks
You can create a function like this:
func getLatAndLong(address : String, competion : #escaping (CLLocationCoordinate2D?) -> ()) {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(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)")
competion(placemark?.location?.coordinate)
}
}
and then call it as :
var location : CLLocationCoordinate2D?
getLatAndLong(address: "pass your address here") { (coordinates) in
self.location = coordinates
}
Now your location variable holds the value.
you can use it to get latitide and longitude
let latitide = location?.latitude
let longitude = location?.longitude
Option 2
You can declare a variable in class and update its you value once you get the coordinates. I would suggest using the above approach as geocoder runs asynchronously. Its better to use a completion handler to know when the geoCoder has finished executing the task and has got the location.
var latitude = 0.0 //
var longitude = 0.0
let geocoder = CLGeocoder()
geocoder.geocodeAddressString("address") { placemarks, error in
let placemark = placemarks?.first
self.latitude = placemark?.location?.coordinate.latitude ?? 0.0
self.longitude = placemark?.location?.coordinate.longitude ?? 0.0
}

Cannot convert value of type 'String?' to expected argument type 'CLLocationDegrees' (aka 'Double')

I try to display the distance between a post creator and the current user within a cell.
My error now is
Cannot convert value of type 'String?' to expected argument type 'CLLocationDegrees' (aka 'Double')
in the following code:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lastLocation = locations.last {
let geoCoder = CLGeocoder()
let myLocation = CLLocation(latitude: lastLocation.coordinate.latitude, longitude: lastLocation.coordinate.longitude)
/*Here the error*/ let myBuddysLocation = CLLocation(latitude: job!.distance, longitude: job!.distance)
let distance = myLocation.distance(from: myBuddysLocation) / 1000
print(String(format: "The distance to my buddy is %.01fkm", distance))
}
}
This is my JobClass:
class Job {
var distance: String?
let ref: DatabaseReference!
init(distance: String? = nil) {
self.distance = distance
ref = Database.database().reference().child("jobs").childByAutoId()
}
init(snapshot: DataSnapshot){
ref = snapshot.ref
if let value = snapshot.value as? [String : Any] {
distance = value["distance"] as? String
}
}
func save() {
let newPostKey = ref.key
let postDictionary = [
"distance" : self.distance!
] as [String : Any]
self.ref.setValue(postDictionary)
}
}
I hope someone knows how to solve it, if you wan't I can add more code if it helps // I'm new to coding
The error is self-explanatory: You are trying to set a String? instead of a CLLocationDegrees, but you can even use a Double type.
There are several ways to fix this:
1) You can change your class definition as it follows:
class Job {
// Change distance to Double and set a default value instead of optional
var distance: Double = 0.0
let ref: DatabaseReference!
// Change the init
init(distance: Double = 0.0) {
self.distance = distance
ref = Database.database().reference().child("jobs").childByAutoId()
}
//You have to change all your distance to Double
init(snapshot: DataSnapshot){
ref = snapshot.ref
if let value = snapshot.value as? [String : Any] {
// So change this cast
distance = value["distance"] as Double
}
}
}
2) You can try a cast from String? to Double, but I don't recommend this.
Anyway, here it is how you can do it:
if let dist = Double(job!.distance){
let myBuddysLocation = CLLocation(latitude: dist, longitude: dist) -> Error Appears in this Line
let distance = myLocation.distance(from: myBuddysLocation) / 1000
print(String(format: "The distance to my buddy is %.01fkm", distance))
}
Actually in your function the only problem are there you passing the string value in place of double you need to change like this.
let myBuddysLocation = CLLocation(latitude: Double.init(job!.distance) ?? 0.0, longitude: Double.init(job!.distance) ?? 0.0)
You need to change your function like this
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lastLocation = locations.last {
let geoCoder = CLGeocoder()
let myLocation = CLLocation(latitude: lastLocation.coordinate.latitude, longitude: lastLocation.coordinate.longitude)
let myBuddysLocation = CLLocation(latitude: Double.init(job!.distance) ?? 0.0, longitude: Double.init(job!.distance) ?? 0.0)
let distance = myLocation.distance(from: myBuddysLocation) / 1000
print(String(format: "The distance to my buddy is %.01fkm", distance))
}
}
It's normal because distance should be Double value. Changing it to: var distance: Double? should correct it.
Or parse the value to Double let myBuddysLocation = CLLocation(latitude: Double(distance)!, longitude: Double(distance)!)
I recommend you to avoid using ! because it could generate a fatal error if the string isn't a number, you can guard the value with guard or if let like this example:
if let latitude = Double(distance) {
let myBuddysLocation = CLLocation(latitude: latitude, longitude: latitude)
}

How do I convert CLLocationCoordinate2D in a string value? [duplicate]

I'm trying to save the coordinates of a user while in one ViewController so that it can be used to create an Annotation that can displayed in another ViewController.
In the view controller that stores the coordinates I'm using the code
NSUserDefaults.standardUserDefaults().setObject( Location, forKey: "Location")
In the map view controller that displays the annotation I'm trying to get the coordinates using the code
let Location = NSUserDefaults.standardUserDefaults().stringForKey("Location")
var Annotation = MKPointAnnotation()
Annotation.coordinate = Location
It is telling me that the value of type String? to a value of type CLLocationCoordinate2D.
So how do I convert the CLLocationCoordinate2D coordinates into a value of type String?
This way you can store Locations to NSUserDefaults:
//First Convert it to NSNumber.
let lat : NSNumber = NSNumber(double: Location.latitude)
let lng : NSNumber = NSNumber(double: Location.longitude)
//Store it into Dictionary
let locationDict = ["lat": lat, "lng": lng]
//Store that Dictionary into NSUserDefaults
NSUserDefaults.standardUserDefaults().setObject(locationDict, forKey: "Location")
After that you can access it this way:
//Access that stored Values
let userLoc = NSUserDefaults.standardUserDefaults().objectForKey("Location") as! [String : NSNumber]
//Get user location from that Dictionary
let userLat = userLoc["lat"]
let userLng = userLoc["lng"]
var Annotation = MKPointAnnotation()
Annotation.coordinate.latitude = userLat as! CLLocationDegrees //Convert NSNumber to CLLocationDegrees
Annotation.coordinate.longitude = userLng as! CLLocationDegrees //Convert NSNumber to CLLocationDegrees
UPDATE:
HERE is your Example project.
extension CLLocationCoordinate2D:Printable
{
init(coords : String)
{
var fullNameArr = split(coords) {$0 == ";"}
self.latitude = NSNumberFormatter().numberFromString(fullNameArr[0])!.doubleValue
self.longitude = (fullNameArr.count > 1) ? NSNumberFormatter().numberFromString(fullNameArr[1])!.doubleValue : 0
}
public var description : String
{
return "\(self.latitude);\(self.longitude)"
}
}
Then use as in your sample code :
var coord = CLLocationCoordinate2D(latitude: 3.2, longitude: 6.4)
NSUserDefaults.standardUserDefaults().setObject(coord.description, forKey: "Location")
var readedCoords = CLLocationCoordinate2D(coords: NSUserDefaults.standardUserDefaults().stringForKey("Location")!)
You can store the latitude or the longitude (or both in a dictionary or a tuple). The way to wrap them in String:
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
var locValue:CLLocationCoordinate2D = manager.location!.coordinate
var lat : String = locValue.latitude.description
var lng : String = locValue.longitude.description
//do whatever you want with lat and lng
}
Using an sprint kind of formatting:
func Coord2String(location : CLLocationCoordinate2D) -> String {
return String(format : "latitude : %f, longitude : %f", location.latitude, location.longitude)
}

Swift: Calling a function with parameters that may be nil

I'm trying to figure out if there is any shorter syntax in Swift for the last line here:
var startPosition: CLLocationCoordinate2D?
var latitude: Double?
var longitude: Double?
// ...
// Here I have skipped some code which may or may not assign values
// to "latitude" and "longitude".
// ...
if latitude != nil && longitude != nil {
startPosition = CLLocationCoordinate2DMake(latitude!, longitude!)
}
As you can see, I want to set the "startPosition" based on "latitude" and "longitude", if those values have been assigned. Otherwise, I accept that the "startPosition" will not be initialized.
I guess this must be possible with "if let" or something similar, but I have failed to figure out how. (I'm experienced in Objective-C, but have just started to learn Swift.)
This is not shorter, but you can simply do
if let latitude = latitude, let longitude = longitude {
startPosition = CLLocationCoordinate2D(latitude: latitude,
longitude: longitude)
}
Notice I used just CLLocationCoordinate2D, not CLLocationCoordinate2DMake. Swift provides constructors without the "make" to most common objects, so you shouldn't usually have to use "make" in constructors.
If you don't want to execute any code after if they are nil use a guard.
var startPosition: CLLocationCoordinate2D?
var latitude: Double?
var longitude: Double?
guard let latitude = latitude && longitude = longitude else {
return
}
startPosition = CLLocationCoordinate2DMake(latitude, longitude)
Clear way
if let latitude = latitude, longitude = longitude {
startPosition = CLLocationCoordinate2D(latitude: latitude,
longitude: longitude)
}
CLLocationCoordinate2D is a struct, it's better if you use the struct initializer. Notice there is only one "let" needed in the if statement.
If i understood the question correctly, you could say
var startPosition: CLLocationCoordinate2D?
var latitude: Double?
var longitude: Double?
if latitude != nil && longitude != nil {
startPosition = CLLocationCoordinate2DMake(latitude!, longitude!)
} else {
startPosition = nil
}

Converting CLLocationCoordinate2D to a String that can be stored

I'm trying to save the coordinates of a user while in one ViewController so that it can be used to create an Annotation that can displayed in another ViewController.
In the view controller that stores the coordinates I'm using the code
NSUserDefaults.standardUserDefaults().setObject( Location, forKey: "Location")
In the map view controller that displays the annotation I'm trying to get the coordinates using the code
let Location = NSUserDefaults.standardUserDefaults().stringForKey("Location")
var Annotation = MKPointAnnotation()
Annotation.coordinate = Location
It is telling me that the value of type String? to a value of type CLLocationCoordinate2D.
So how do I convert the CLLocationCoordinate2D coordinates into a value of type String?
This way you can store Locations to NSUserDefaults:
//First Convert it to NSNumber.
let lat : NSNumber = NSNumber(double: Location.latitude)
let lng : NSNumber = NSNumber(double: Location.longitude)
//Store it into Dictionary
let locationDict = ["lat": lat, "lng": lng]
//Store that Dictionary into NSUserDefaults
NSUserDefaults.standardUserDefaults().setObject(locationDict, forKey: "Location")
After that you can access it this way:
//Access that stored Values
let userLoc = NSUserDefaults.standardUserDefaults().objectForKey("Location") as! [String : NSNumber]
//Get user location from that Dictionary
let userLat = userLoc["lat"]
let userLng = userLoc["lng"]
var Annotation = MKPointAnnotation()
Annotation.coordinate.latitude = userLat as! CLLocationDegrees //Convert NSNumber to CLLocationDegrees
Annotation.coordinate.longitude = userLng as! CLLocationDegrees //Convert NSNumber to CLLocationDegrees
UPDATE:
HERE is your Example project.
extension CLLocationCoordinate2D:Printable
{
init(coords : String)
{
var fullNameArr = split(coords) {$0 == ";"}
self.latitude = NSNumberFormatter().numberFromString(fullNameArr[0])!.doubleValue
self.longitude = (fullNameArr.count > 1) ? NSNumberFormatter().numberFromString(fullNameArr[1])!.doubleValue : 0
}
public var description : String
{
return "\(self.latitude);\(self.longitude)"
}
}
Then use as in your sample code :
var coord = CLLocationCoordinate2D(latitude: 3.2, longitude: 6.4)
NSUserDefaults.standardUserDefaults().setObject(coord.description, forKey: "Location")
var readedCoords = CLLocationCoordinate2D(coords: NSUserDefaults.standardUserDefaults().stringForKey("Location")!)
You can store the latitude or the longitude (or both in a dictionary or a tuple). The way to wrap them in String:
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
var locValue:CLLocationCoordinate2D = manager.location!.coordinate
var lat : String = locValue.latitude.description
var lng : String = locValue.longitude.description
//do whatever you want with lat and lng
}
Using an sprint kind of formatting:
func Coord2String(location : CLLocationCoordinate2D) -> String {
return String(format : "latitude : %f, longitude : %f", location.latitude, location.longitude)
}

Resources