I want to get the location of the user when press a button, I found a way to do that with CLLocationManager, that code work perfectly until the device the device doesn't have network connection, the function func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) catch an error
((NSError?) error = domain: "kCLErrorDomain" - code: 2 { _userInfo =
nil })
How can I get the device location without internet connection? here is the code I use
import CoreLocation
class SesionViewController: UIViewController, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: { (placemarks, error) -> Void in
if (error != nil) {
print("Error:" + error!.localizedDescription)
return
}
if placemarks!.count > 0 {
let pm = placemarks![0] as CLPlacemark
print(pm!.coordinate.latitude)
}else {
print("Error with data")
}
})
}
func startButtonPress(sender:UIButton!){
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
self.locationManager.stopUpdatingLocation()
}
}
Edit
I already found the solution, I just don't execute CLGeocoder().reverseGeocodeLocation... just catch the location with the locations var like this
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print(manager.location!.coordinate.latitude)
}
from Apple doc about ClGeocoder
The computer or device must have access to the network in order for
the geocoder object to return detailed placemark information
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: { (placemarks, error) -> Void in
if (error != nil) {
print("Error:" + error!.localizedDescription)
return
}
if placemarks!.count > 0 {
let pm = placemarks![0] as CLPlacemark
print(pm!.coordinate.latitude)
}else {
print("Error with data")
}
})
will always report an error without an internet access. Your location data are still available (if your device has GPS unit)
Related
First I have a global variable:
var userCity : String?
Using reverseGeocodeLocation() to obtain the city name from the user's location, I am trying to store that city name information into the userCity global variable. I know that you cannot simply return data from asynchronous tasks. Instead, you would have to pass back via a block. This is what I have so far after looking at other StackOverflow posts:
//get user's location
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[locations.count - 1]
if location.horizontalAccuracy > 0 {
locationManager.stopUpdatingLocation()
locationManager.delegate = nil
reverseCoordinatesToCity(location: location) { (city) in
self.userCity = city
}
}
}
func reverseCoordinatesToCity(location: CLLocation, completion: #escaping (_ city: String?) -> Void) {
CLGeocoder().reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
if (error != nil) {
print("Reverse geocoder failed with an error: " + (error?.localizedDescription)!)
completion("")
} else if (placemarks?.count)! > 0 {
let pm = placemarks![0] as CLPlacemark
completion(pm.locality!)
} else {
print("Problems with the data received from geocoder.")
completion("")
}
})
}
When I try to print userCity in a different method. For example:
func someFunction() {
print(userCity!)
}
Nothing is printed out and it seems that userCity is nil. I've spent almost three hours trying to figure this out. My main goal is to pass this data onto another ViewController using a segue.
Any help is appreciated. Thank you.
As #rmaddy has said you are not using city when its ready. The way to fix that is to hinge your view controller logic from the CLGeocoder callback. It's also worth setting your location manager to nil if you don't want to use it anymore because stopUpdatingLocations() doesn't always stop location updates dead in its tracks.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[locations.count - 1]
if location.horizontalAccuracy > 0 {
locationManager.stopUpdatingLocation()
locationManager.delegate = nil
locationManager = nil //this makes sure you don't get more updates
reverseCoordinatesToCity(location: location)
}
}
func reverseCoordinatesToCity(location: CLLocation) {
CLGeocoder().reverseGeocodeLocation(location, completionHandler: {(placemarks, error) -> Void in
if (error != nil) {
print("Reverse geocoder failed with an error: " + (error?.localizedDescription)!)
} else if (placemarks?.count)! > 0 {
let pm = placemarks![0] as CLPlacemark
city = pm.locality!
//use city here with your view controllers
} else {
print("Problems with the data received from geocoder.")
}
})
}
I have tried this in swift
How to get the current location in watchOS 2?
But It's not working
Here is my code in WKInterfaceController
override func awake(withContext context: Any?) {
super.awake(withContext: context)
self.setupLocation()
}
extension InterfaceController: CLLocationManagerDelegate {
func setupLocation() {
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let currentLocation = locations.first else { return }
self.currentLocation = currentLocation
let location = "lat: \(self.currentLocation.coordinate.latitude)\nlong: \(self.currentLocation.coordinate.longitude)"
self.locationLabel.setText(location)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("LOCATION MANAGER DID FAIL ERROR : \(error)")
}
}
I have added permission in WatchExtension and Main App info.plist as well.
It's still not even asking permission alert.
What i'm missing ???? :(
For some reason Xcode thinks I'm not implementing didFailWithError method of the CLLocationManagerDelegate protocol
I fail to see what I'm doing wrong, as I literally copied from another SO post that said this didFailWithErrormethod was updated for Swift 3. So I don't understand why Xcode thinks I'm not implementing didFailWithError
Any insight would be greatly appreciated!
Code
class OptionsViewController: UIViewController,
CLLocationManagerDelegate {
var locationManager: CLLocationManager = CLLocationManager()
override func viewDidLoad() {
//Ask user for location
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
//Use users current location if no starting point set
if CLLocationManager.locationServicesEnabled() {
if CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse
|| CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways {
locationManager.requestLocation()
}
else{
locationManager.requestWhenInUseAuthorization()
}
}
else{
//Alert user to open location service, bra bra bra here...
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("error:: \(error)")
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
print("didChangeAuthorization")
if status == CLAuthorizationStatus.authorizedWhenInUse
|| status == CLAuthorizationStatus.authorizedAlways {
locationManager.requestLocation()
}
else{
//other procedures when location service is not permitted.
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("Did update location called")
// let locValue:CLLocationCoordinate2D = manager.location!.coordinate
// print("locations = \(locValue.latitude) \(locValue.longitude)")
if locations.first != nil {
print("location:: (location)")
}
}
Aaaandd I realized it's b/c I needed to use an Error of a specific type (specifically Swift.Error)
This is the right method declaration for didFailWithError:
func locationManager(_ manager: CLLocationManager, didFailWithError error: Swift.Error) {
I am using Corelocation in several views for which I use the following code :
import UIKit
import CoreLocation
class ModelCL: UIViewController, CLLocationManagerDelegate {
let LocationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
//CoreLocation
self.LocationManager.delegate = self
self.LocationManager.desiredAccuracy = kCLLocationAccuracyBest
self.LocationManager.requestWhenInUseAuthorization()
self.LocationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {
(placemarks, error) -> Void in
if error != nil {
print("Error")
}
if let pm = placemarks?.first {
self.displayLocationInfo(pm)
}
else {
print("Error : data error")
}
})
}
func displayLocationInfo (placemark: CLPlacemark) {
self.LocationManager.stopUpdatingLocation()
print(placemark.subThoroughfare)
print(placemark.thoroughfare)
print(placemark.locality)
print(placemark.postalCode)
print(placemark.subAdministrativeArea)
print(placemark.administrativeArea)
print(placemark.country)
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Error :" + error.localizedDescription)
}
}
It works well on each view as I implemented it on each of them. Wouldn't it better to create only one Model Class with this code and call the administrative level needed on each view? I can't figure out how to call it on another view...
It's weird. There are some devices that crash and some other devices that not. The thing is when having location not activated the app never dies but when I allow my app access to the location in some devices crash and in other devices not.
This is the code:
override func viewDidAppear(animated: Bool) {
if CLLocationManager.locationServicesEnabled(){
switch CLLocationManager.authorizationStatus() {
case .NotDetermined, .Restricted, .Denied:
print("No access")
case .AuthorizedAlways, .AuthorizedWhenInUse:
let geocoder = CLGeocoder()
longitude = self.locationManager.location!.coordinate.longitude
latitude = self.locationManager.location!.coordinate.latitude
geocoder.reverseGeocodeLocation(CLLocation(latitude: (latitude), longitude: (longitude)), completionHandler: {placemarks, error in
if error == nil && placemarks!.count > 0 {
self.thoroughfare = (placemarks!.last?.thoroughfare)!
self.city = (placemarks!.last?.locality)!
print(self.thoroughfare)
print(self.city)
print(self.longitude)
print(self.latitude)
}
})
}
} else {
print("Location services are not enabled")
}
}
When app crashes the error points to this line:
longitude = self.locationManager.location!.coordinate.longitude
latitude = self.locationManager.location!.coordinate.latitude
I've tested the app in 10 devices, having 1-2 of them that crashes at this point.
What's happening? I think I'm managing rightly what to do and what no to do when location is or not is allowed.
You should chek if
self.locationManager.location
Is null before using it
Please try this whole code to get the location and its details.Its tried and working solution in Swift 3.0
import CoreLocation
import Foundation
class ViewController: UIViewController,CLLocationManagerDelegate {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
findMyLocation()
}
func findMyLocation(){
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)->Void in
if (error != nil) {
print("Reverse geocoder failed with error" + error!.localizedDescription)
return
}
if placemarks!.count > 0 {
let pm = placemarks![0]
self.displayLocationInfo(pm)
} else {
print("Problem with the data received from geocoder")
}
})
}
func displayLocationInfo(_ placemark: CLPlacemark?) {
if let containsPlacemark = placemark {
//stop updating location to save battery life
locationManager.stopUpdatingLocation()
let locality = (containsPlacemark.locality != nil) ? containsPlacemark.locality : ""
let postalCode = (containsPlacemark.postalCode != nil) ? containsPlacemark.postalCode : ""
let administrativeArea = (containsPlacemark.administrativeArea != nil) ? containsPlacemark.administrativeArea : ""
let country = (containsPlacemark.country != nil) ? containsPlacemark.country : ""
print(" Postal Code \(postalCode)")
print(" administrativeArea \(administrativeArea)")
print(" country \(country)")
print(" locality \(locality)")
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error while updating location " + error.localizedDescription)
}
Thank you
Don't declare optional values to variable. always handle errors
Don't unwrap the location
self.locationManager.location // Your error
if var longitude = self.locationManager.location.coordinate.longitude {
// do your thing
}else {
// handle the error by declaring default value
}
second thing you also might receive null values even if user lost internet while getting the location or you forget the simulate the location while testing in simulator so always handle the error
Please Check your didUpdateLocations method, you just need to check whether location is getting correctly or getting nil.
if ((self.locationManager.location) != nil){
//Get Location access here.
}