I've used this code for my App. Its works great but sometime crashes, after +/- 50 secondes of tracking my route. I know it has something to do with the optionals "?", but I can't get it to work.
I get the following message:
fatal error: unexpectedly found nil while unwrapping an Optional
Part where the code breaks:
if let locationName = placeMark.addressDictionary?["Name"] as? NSString
{
print(locationName)
self.locationName = locationName as String
}
Full locationManager code:
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
for location in locations as [CLLocation] {
let howRecent = location.timestamp.timeIntervalSinceNow
//start motion tracker
motionTracker()
//location name tracker
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
let latitude: CLLocationDegrees = locValue.latitude
let longitude: CLLocationDegrees = locValue.longitude
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: latitude, longitude: longitude)
geoCoder.reverseGeocodeLocation(location)
{
(placemarks, error) -> Void in
let placeArray = placemarks as [CLPlacemark]!
// Place details
var placeMark: CLPlacemark!
placeMark = placeArray?[0]
// Address dictionary
print(placeMark.addressDictionary)
// Location name
if let locationName = placeMark.addressDictionary?["Name"] as? NSString
{
print(locationName)
self.locationName = locationName as String
}
// Street address
if let street = placeMark.addressDictionary?["Thoroughfare"] as? NSString
{
//print(street)
self.locationStreet = street as String
}
// City
if let city = placeMark.addressDictionary?["City"] as? NSString
{
self.locationCity = city as String
//print(city)
}
// Zip code
if let zip = placeMark.addressDictionary?["ZIP"] as? NSString
{
//print(zip)
}
// Country
if let country = placeMark.addressDictionary?["Country"] as? NSString
{
//print(country)
}
}
I suspect you’re hitting a case where Google doesn’t have a reverse geocode result for your location and is returning an empty array.
Instead of:
let placeArray = placemarks as [CLPlacemark]!
var placemark: CLPlacemark!
placemark = placeArray?[0]
… which assumes that there will be an array and it will always contain at least one element, use:
if let placemark = placemarks?.first {
// Rest of your code
}
Related
I'm trying to get city name from my current location coordiate by using CLGeocoder().reverseGeocodeLocation.
It gives me country name, street name, state and many other things but not city. Is there anything wrong with my code?
Here's my code:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
CLGeocoder().reverseGeocodeLocation(location) { (placeMark, error) in
if error != nil{
print("Some errors: \(String(describing: error?.localizedDescription))")
}else{
if let place = placeMark?[0]{
print("country: \(place.administrativeArea)")
self.lblCurrentLocation.text = place.administrativeArea
}
}
} }
I use the below code too. But doesn't work for me. Here's the another way.
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: (self.locationManager.location?.coordinate.latitude)!, longitude: (self.locationManager.location?.coordinate.longitude)!)
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
// Address dictionary
print(placeMark.addressDictionary as Any)
// Location name
if let locationName = placeMark.addressDictionary!["Name"] as? NSString {
print("locationName: \(locationName)")
}
// Street address
if let street = placeMark.addressDictionary!["Thoroughfare"] as? NSString {
print("street: \(street)")
}
// City
if let city = placeMark.addressDictionary!["City"] as? NSString {
print("city : \(city)")
}
// Zip code
if let zip = placeMark.addressDictionary!["ZIP"] as? NSString {
print("zip :\(zip)")
}
// Country
if let country = placeMark.addressDictionary!["Country"] as? NSString {
print("country :\(country)")
}
})
Please someone help me to get city name.
The field is called locality
if let locality = placeMark.addressDictionary!["locality"] as? NSString {
print("locality :\(locality)")
}
Locality Apple docs
https://developer.apple.com/documentation/corelocation/clplacemark/1423507-locality?language=objc
CLPlacemark
https://developer.apple.com/documentation/corelocation/clplacemark?language=objc
Update:
Try this
import Foundation
import CoreLocation
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: 40.730610, longitude: -73.935242) // <- New York
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, _) -> Void in
placemarks?.forEach { (placemark) in
if let city = placemark.locality { print(city) } // Prints "New York"
}
})
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?)
}
}
SO, I am new to swift and I made the conversion from current Lat and Long to City name and Country, it works fine like that:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
if didFindLocation == false
{
didFindLocation = true
locationManager.stopUpdatingLocation()
userLocation = locations[0]
long = userLocation.coordinate.longitude;
lat = userLocation.coordinate.latitude;
print("\(lat),\(long)")
converLocationToCity()
}
}
func converLocationToCity()
{
let geoCoder = CLGeocoder()
userLocation = CLLocation(latitude: self.lat, longitude: self.long)
geoCoder.reverseGeocodeLocation(userLocation, completionHandler:
{
(placemarks, error) -> Void in
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
if let city = placeMark.addressDictionary!["State"] as? String
{
self.city = city as String
} else
{
self.city = ""
}
if let country = placeMark.addressDictionary!["Country"] as? String
{
self.country = country as String
} else
{
self.country = ""
}
self.currentCity.name = ("\(self.city), \(self.country)" as String)
print("\(self.currentCity.name)")
self.fetchWeather.performCurrentWeatherFetch(forSelectedCity: self.currentCity.name)
DispatchQueue.main.async()
{
(self.superview as! UICollectionView).reloadData()
}
})
}
But when the device is set to other language, Russian for example it returns me the City Name and Country in Russian characters, but I need it to be only in english, please anybody some ideas or suggestions? Thank you!
Here is My Solution
While getting the location data i change `UserDefaults.standard.set(["base"], forKey: "AppleLanguages")'
and once I have received the dictionary in English i remove the Object
UserDefaults.standard.removeObject(forKey: "AppleLanguages")
which then sets applelanguage to default value
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0] as CLLocation
// Call stopUpdatingLocation() to stop listening for location updates,
// other wise this function will be called every time when user location changes.
// manager.stopUpdatingLocation()
print("user latitude = \(userLocation.coordinate.latitude)")
print("user longitude = \(userLocation.coordinate.longitude)")
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: userLocation.coordinate.latitude, longitude: userLocation.coordinate.longitude)
//location.accessibilityLanguage = "en-US"
UserDefaults.standard.set(["base"], forKey: "AppleLanguages")
geoCoder.reverseGeocodeLocation(location, completionHandler: { placemarks, error in
guard let addressDict = placemarks?[0].addressDictionary else {
return
}
print(addressDict)
// Print each key-value pair in a new row
addressDict.forEach { print($0) }
// Print fully formatted address
if let formattedAddress = addressDict["FormattedAddressLines"] as? [String] {
print(formattedAddress.joined(separator: ", "))
}
// Access each element manually
if let locationName = addressDict["Name"] as? String {
print(locationName)
}
if let street = addressDict["Thoroughfare"] as? String {
print(street)
}
var myCity:String = ""
if let city = addressDict["City"] as? String {
print(city)
if(city != "" ){
myCity = city
}
}
if let zip = addressDict["ZIP"] as? String {
print(zip)
}
var myCountry:String = ""
if let country = addressDict["Country"] as? String {
print(country)
if(country != "" ){
myCountry = country
}
MyGenericFunctions.sharedInstance.saveCountry(country: country)
}
manager.stopUpdatingLocation()
if(myCity != "" && myCountry != "" && self.isCurrLocAPICalled != true){
print("API Called")
self.isCurrLocAPICalled = true
self.callLocationSearch(strCity: myCity, strCountry: myCountry)
UserDefaults.standard.removeObject(forKey: "AppleLanguages")
}
})
//manager.stopUpdatingLocation()
}
Below codes from Google only return a place if I pick one from the list like the one I attached.
My question:
Is there any function available for me to store all the place's detail in a given coordinate? For example, if I have a coordinate of (51.5108396, -0.0922251), how can I get all the information of nearby places? I am not familiar with Json. Is there any example close to what I want? Thanks a lot.
This function placesClient.currentPlaceWithCallback is somehow close to what I want but it cannot use custom coordinate because it uses user's current coordinate.
//https://developers.google.com/places/ios-api/placepicker
let center = CLLocationCoordinate2DMake(51.5108396, -0.0922251)
let northEast = CLLocationCoordinate2DMake(center.latitude + 0.001, center.longitude + 0.001)
let southWest = CLLocationCoordinate2DMake(center.latitude - 0.001, center.longitude - 0.001)
let viewport = GMSCoordinateBounds(coordinate: northEast, coordinate: southWest)
let config = GMSPlacePickerConfig(viewport: viewport)
let placePicker = GMSPlacePicker(config: config)
placePicker?.pickPlaceWithCallback({ (place: GMSPlace?, error: NSError?) -> Void in
if let error = error {
print("Pick Place error: \(error.localizedDescription)")
return
}
if let place = place {
print("Place name \(place.name)")
print("Place address \(place.formattedAddress)")
print("Place attributions \(place.attributions)")
} else {
print("No place selected")
}
})
Fetching nearby places using google maps
Something is changed due to upgraded iOS version.
complete changed code
func fetchPlacesNearCoordinate(coordinate: CLLocationCoordinate2D, radius: Double, types:[String]) {
var urlString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?key=\("your api key")&location=\(coordinate.latitude),\(coordinate.longitude)&radius=\(radius)&rankby=prominence&sensor=true"
let typesString = types.count > 0 ? types.joinWithSeparator("|") : "food"
urlString += "&types=\(typesString)"
urlString = urlString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
let session = NSURLSession.sharedSession()
let placesTask = session.dataTaskWithURL(NSURL(string: urlString)!) {data, response, error in
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
if let jsonResult = (try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as? NSDictionary {
let returnedPlaces: NSArray? = jsonResult["results"] as? NSArray
if returnedPlaces != nil {
for index in 0..<returnedPlaces!.count {
if let returnedPlace = returnedPlaces?[index] as? NSDictionary {
var placeName = ""
var latitude = 0.0
var longitude = 0.0
if let name = returnedPlace["name"] as? NSString {
placeName = name as String
}
if let geometry = returnedPlace["geometry"] as? NSDictionary {
if let location = geometry["location"] as? NSDictionary {
if let lat = location["lat"] as? Double {
latitude = lat
}
if let lng = location["lng"] as? Double {
longitude = lng
}
}
}
print("index", index, placeName, latitude, longitude)
}
}
}
}
}
placesTask.resume()
}
I have a class named Location that has several methods in it that do not have any parameters.
However, when I try to create a variable with the result of the method, it wants an argument. Why is that?
Location class:
let locationManager = CLLocationManager()
public class Location {
public func coordinate() -> (latitude: Float?, longitude: Float?) {
let latitude = Float((locationManager.location?.coordinate.latitude)!)
let longitude = Float((locationManager.location?.coordinate.longitude)!)
return (latitude: latitude, longitude: longitude)
}
public func getCity() -> String {
var returnCity: String = "N/A"
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
// City
if let city = placeMark.addressDictionary!["City"] as? String {
returnCity = city
}
})
return returnCity
}
public func getCountry() -> String {
var returnCountry: String = "N/A"
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
// City
if let country = placeMark.addressDictionary!["Country"] as? String {
returnCountry = country
}
})
return returnCountry
}
public func getZip() -> Int {
var returnZip: Int = 0
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
// City
if let zip = placeMark.addressDictionary!["ZIP"] as? Int {
returnZip = zip
}
})
return returnZip
}
public func getLocationName() -> String {
var returnName: String = "N/A"
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
// City
if let locationName = placeMark.addressDictionary!["Name"] as? String {
returnName = locationName
}
})
return returnName
}
public func getStreetAddress() -> String {
var returnAddress: String = "N/A"
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: (locationManager.location?.coordinate.latitude)!, longitude: (locationManager.location?.coordinate.longitude)!)
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
// City
if let street = placeMark.addressDictionary!["Thoroughfare"] as? String {
returnAddress = street
}
})
return returnAddress
}
}
Trying to create a variable:
let city = Location.getCity()
Here are some screen shots of what I get:
These methods are not class methods, they are instance methods. You must call them on an instance of the Location class, not on the class itself. Evidently, Swift can call instance methods similarly to Python: the method is a function owned by the class, and its argument is an instance of the class. But you should not call instance methods this way.
The best way to solve this problem is to construct a Location object and then call the method on it:
let city: Location = Location().getCity()
Because you're trying to call it as a class function. You should be creating an instance of Location and calling the function on that. Note also that it returns String Where your code is telling the compiler you're expecting it to return a Location.