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()
}
Related
I try to save a map with the users location to a post, but I get Value of type 'MKMapView?' has no member 'MKMapView' as an error all the time...
The following shows my code but I leave out any background code to the images and labels as everything there works fine, I just include them in here so you know how I save the post informations... Do you know what my error is and how I can solve it?
var takenMap: MKMapView!
#IBAction func postPressed(_ sender: Any) {
if textView.text != "" && takenImage != nil && userLocation.text != "" {
// Create and save a new job
let newJob = Job(text: textView.text, jobImage: takenImage!, addedByUser: (userLabel?.text)!, userImage: UserImage, location: userLocation.text, map: takenMap.MKMapView)
newJob.save()
}
//MARK:- CLLocationManager Delegates
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lastLocation = locations.last {
let geoCoder = CLGeocoder()
let center = CLLocationCoordinate2D(latitude: lastLocation.coordinate.latitude, longitude: lastLocation.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
map.setRegion(region, animated: true)
self.map = takenMap
geoCoder.reverseGeocodeLocation(lastLocation) { (placeMarks, error) in
if error == nil {
if let firstLocation = placeMarks?[0] {
self.locationManager.stopUpdatingLocation()
if let cityName = firstLocation.locality,
let street = firstLocation.thoroughfare {
self.scanLocation = "\(street), \(cityName)"
print("This is the current city name", cityName)
print("this is the current street address", street)
self.takenLocation = self.scanLocation!
self.userLocation.text = self.takenLocation
}
}
}
}
}
}
Job.swift:
var map: String?
init(map: String? = nil) {
self.map = map
ref = Database.database().reference().child("jobs").childByAutoId()
}
init(snapshot: DataSnapshot){
ref = snapshot.ref
if let value = snapshot.value as? [String : Any] {
map = value["location"] as? String
}
}
func save() {
let newPostKey = ref.key
// save jobImage
if let imageData = jobImage?.jpegData(compressionQuality: 0.5) {
let storage = Storage.storage().reference().child("jobImages/\(newPostKey)")
storage.putData(imageData).observe(.success, handler: { (snapshot) in
self.downloadURL = snapshot.metadata?.downloadURL()?.absoluteString
let postDictionary = [
"map" : self.map!
] as [String : Any]
self.ref.setValue(postDictionary)
})
}
}
I left out any code for labels or whatever out so the snippet won't be too long
The code takenMap.MKMapView should probably just be takenMap.
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"
}
})
I am trying to set up a function to get my current location in app delegate but when I print(city) at the bottom it returns the original initialized value in the global variable which is "hello", even though I updated the value under the CLGeocoder.
AppDelegate:
import UIKit
import CoreData
import CoreLocation
let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
var country = "hello"
var city = "hello"
func setupLocationManager(){
let locationManager = CLLocationManager()
locationManager.requestAlwaysAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
// Below method will provide you current location.
func getLocation() -> [String]{
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
manager.requestAlwaysAuthorization()
manager.startUpdatingLocation()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestAlwaysAuthorization()
manager.startUpdatingLocation()
let selflocation = manager.location
let latitude: Double = selflocation!.coordinate.latitude
let longitude: Double = selflocation!.coordinate.longitude
print("current latitude :: \(latitude)")
print("current longitude :: \(longitude)")
let location = CLLocation(latitude: latitude, longitude: longitude) //changed!!!
CLGeocoder().reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
print(location)
if error != nil {
print("Reverse geocoder failed with error" + (error?.localizedDescription)!)
}
let pm = placemarks![0]
let speed = (selflocation?.speed)!
city = pm.addressDictionary!["City"]! as! String
country = pm.addressDictionary!["Country"]! as! String
if (placemarks?.count)! > 0 {
}
else {
print("Problem with the data received from geocoder")
}
})
print(city)
return [city as! String, country as! String]
}
This is because the geocoding is done asynchronously, so the print(city) is being executed before the geocoding is completed. So I suggest you do this.
func getLocation(completion: #escaping (Array<String>)->()){
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestAlwaysAuthorization()
manager.startUpdatingLocation()
let selflocation = manager.location
let latitude: Double = selflocation!.coordinate.latitude
let longitude: Double = selflocation!.coordinate.longitude
let location = CLLocation(latitude: latitude, longitude: longitude)
CLGeocoder().reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
if let error = error {
print(error.localizedDescription)
return
}
if let placemark = placemarks?.first {
if let country = placemark.country, let city = placemark.locality {
completion([city, country])
return
} else {
print("country or city was nil.")
}
} else {
print("Problem with the data received from geocoder")
}
})
}
So instead of calling getLocation() call
getLocation { (location) in
print(location)
}
The problem here is you are getting the value before a new location value is assigned to it. You have to wait a little bit to get the updated value.
reverseGeocodeLocation works asynchronously, so the print statement is actually happening before it finishes. If you have any logic that depends on the results, you'll probably need to put it inside the completion handler closure.
Like the other answer says, reverseGeocodeLocation works asynchronously, so you may want to move the print(city) inside the closure, such as after
else {
print("Problem with the data received from geocoder")
}
Originally I was trying to save a CLLocation as a NSUserDefault value before I stored it as a CKRecord in CLoudkit but I got the error: "defaults.setObject(locationRecord.recordID, forKey: "locationRecordID")" with the reason being "Attempt to set a non-property-list object as an NSUserDefaults/CFPreferences value for key locationRecordID". So now I am trying to save the lat and long as a default and replace the old location in Cloudkit. I am currently getting a 'Thread 1 SIGABRT' error on the line "publicDB.fetchRecordWithID((defaults.objectForKey("Location") as! CKRecordID),completionHandler: " with the reason being "Could not cast value of type '__NSCFDictionary' (0x1a1bec968) to 'CKRecordID'."
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations.last
self.loc1 = location!
let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.004, longitudeDelta: 0.004))
self.mapView.setRegion(region, animated: true)
self.locationManager.stopUpdatingLocation()
self.locationManager.startMonitoringSignificantLocationChanges()
self.getRecordToUpdate(locations)
let lat: CLLocationDegrees = center.latitude
self.lat1 = lat
let long: CLLocationDegrees = center.longitude
self.long1 = long
locationRecord.setObject(location, forKey: "location")
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveRecord(locationRecord) { record, error in
}
if error == nil
{
print("Location saved")
}
}
func getRecordToUpdate(locations:[CLLocation])
{
let defaults = NSUserDefaults.standardUserDefaults()
var locationRecord:CKRecord
if defaults.objectForKey("Location") == nil{
locationRecord = CKRecord(recordType: "location")
let locationDict = ["lat": lat1, "lng": long1]//
defaults.setObject(locationDict, forKey: "Location")//
self.updateLocationRecord(locationRecord, locations: locations)
print("new")
}else{
let publicDB = CKContainer.defaultContainer().publicCloudDatabase
publicDB.fetchRecordWithID((defaults.objectForKey("Location") as! CKRecordID),completionHandler: {
(record, error) in
if error == nil{
self.updateLocationRecord(record!, locations: locations)
print("fetched record")
}else{
print("Error fetching previous record")
}
})
}
}
func updateLocationRecord(locationRecord:CKRecord, locations:[CLLocation])
{
let location = locations.last
locationRecord.setObject(location, forKey: "location")
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveRecord(locationRecord) { record, error in
}
if error == nil
{
print("Location saved")
}
}
Try this
You can save NSData to NSUserDefaults. So all you need is to convert your CLLocation object to NSData as follow:
// saving your CLLocation object
let locationData = NSKeyedArchiver.archivedDataWithRootObject(your location here)
NSUserDefaults.standardUserDefaults().setObject(locationData, forKey: "locationData")
// loading it
if let loadedData = NSUserDefaults.standardUserDefaults().dataForKey("locationData") {
if let loadedLocation = NSKeyedUnarchiver.unarchiveObjectWithData(loadedData) as? CLLocation {
println(loadedLocation.coordinate.latitude)
println(loadedLocation.coordinate.longitude)
}
}
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
}