iOS - CLLocation Manager didUpdateLocations being called in one class but not another? - ios

Hi I'm making a program that gets the users location and puts an according annotation on the map. I started by writing all of the code in the View Controller and it gets the location perfectly. Below is the working code in the view controller.
class MapViewController: UIViewController, CLLocationManagerDelegate {
var annotation = MKPointAnnotation()
var userLocation: CLLocation?
#IBOutlet weak var mapView: MKMapView!
var locationManager:CLLocationManager!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
determineCurrentLocation()
}
func determineCurrentLocation() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
userLocation = locations[0] as CLLocation
print("user latitude = \(userLocation?.coordinate.latitude)")
print("user longitude = \(userLocation?.coordinate.longitude)")
annotation.coordinate = CLLocationCoordinate2D(latitude: (userLocation?.coordinate.latitude)!, longitude: (userLocation?.coordinate.longitude)!)
annotation.title = "You"
mapView.addAnnotation(annotation)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error \(error)")
}
However now when I try and recreate almost the exact same code in another swift file. didUpdateLocations never gets called. locationManager.startUpdatingLocation() does get called.
Below is my new swift file which I call from the View Controller. Is there any simple concept I'm missing here because I really don't see why this doesn't work.
import Foundation
import CoreLocation
class SendLocation: NSObject, CLLocationManagerDelegate {
var userLocation: CLLocation?
var locationManager:CLLocationManager!
func sendLocationPost() {
determineCurrentLocation()
}
func determineCurrentLocation() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
print("WHY")
if CLLocationManager.locationServicesEnabled(){
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
userLocation = locations[0] as CLLocation
print("user latitude = \(userLocation?.coordinate.latitude)")
print("user longitude = \(userLocation?.coordinate.longitude)")
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error \(error)")
}
}
I call it using :
let location = SendLocation()
location.sendLocationPost()`
in my View Controller

This happens because you are not keeping a reference to your SendLocation object.
Make SendLocation a property of your UIViewController.
For example, calling it from a static scope will not keep a reference.
WONT WORK:
static func sendLocation() {
let location = SendLocation()
location.sendLocationPost()
}
WILL WORK
let location = SendLocation()
func sendLocation() {
location.sendLocationPost()
}

Related

fatal error: unexpectedly found nil while unwrapping an Optional value when getting Location?

I am developing an app using swift I want to fetch current location of the user and want to show in google map so I wrote my code in my class I have included all the functions and methods (i.e) added and imported frameworks core location and also updated plist but I can't able to fetch current location rotationally some time I get the location but some time it crashed.
Here is the code what I am tried.
import CoreLocation
class HomeViewController: UIViewController, CLLocationManagerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.startUpdatingLocation()
let location: CLLocation? = locationManager.location
let coordinate: CLLocationCoordinate2D? = location?.coordinate ----> App crossed this line then it will crashed
print(coordinate!)
print(coordinate!.latitude)
print(coordinate!.longitude)
strForCurLatitude = "\(coordinate!.latitude)"
strForCurLongitude = "\(coordinate!.longitude)"}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
print("User allowed us to access location")
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error while get location \(error)")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locationManager.startUpdatingLocation()
let locationNew = locations.last
let coordinateNew: CLLocationCoordinate2D? = locationNew?.coordinate
strForCurLatitude = "\(coordinateNew!.latitude)"
strForCurLongitude = "\(coordinateNew!.longitude)"
strForLat=strForCurLatitude;
strForLong=strForCurLongitude;
print("User's Latitude is: \(Double(strForLat!)!)")
print("User's Longitude is: \(Double(strForLong!)!)")
self.locationManager.startUpdatingLocation()
}
Remove this from ViewDidLoad because you can not get location in viewDidLoad.
let location: CLLocation? = locationManager.location
let coordinate: CLLocationCoordinate2D? = location?.coordinate
print(coordinate!)
print(coordinate!.latitude)
print(coordinate!.longitude)
strForCurLatitude = "\(coordinate!.latitude)"
strForCurLongitude = "\(coordinate!.longitude)"
In CLLocationManager's following method best for getting location
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
make a reference to CLLocationManager as variable, like this:
class HomeViewController {
var locationManager = CLLocationManager()
viewDidLoad() {
super.viewDidLoad()
}
// rest of code
}
in this case Location Manager should works properly.

How to fetch current location when users move in swift [duplicate]

I want to get the current longitude and latitude of a location using Swift and display them via labels. I tried to do this but nothing displays on the labels.
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate{
#IBOutlet weak var longitude: UILabel!
#IBOutlet weak var latitude: UILabel!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
if (CLLocationManager.locationServicesEnabled()) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
} else {
println("Location services are not enabled");
}
}
// MARK: - CoreLocation Delegate Methods
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
locationManager.stopUpdatingLocation()
removeLoadingView()
if (error) != nil {
print(error)
}
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var locationArray = locations as NSArray
var locationObj = locationArray.lastObject as CLLocation
var coord = locationObj.coordinate
longitude.text = coord.longitude
latitude.text = coord.latitude
longitude.text = "\(coord.longitude)"
latitude.text = "\(coord.latitude)"
}
}
IMHO, you are over complicating your code when the solution you are looking is pretty simple.
I have done it by using the following code:
First create an instance of CLLocationManager and Request Authorization
var locManager = CLLocationManager()
locManager.requestWhenInUseAuthorization()
then check if the user allowed authorization.
var currentLocation: CLLocation!
if
CLLocationManager.authorizationStatus() == .authorizedWhenInUse ||
CLLocationManager.authorizationStatus() == .authorizedAlways
{
currentLocation = locManager.location
}
to use it just do this
label1.text = "\(currentLocation.coordinate.longitude)"
label2.text = "\(currentLocation.coordinate.latitude)"
Your idea of setting them to the label.text is correct, however the only reason I can think of is that the user is not giving you permission and that is why your current Location data will be nil.
However you would need to debug and tell us that.
Also the CLLocationManagerDelegate is not necessary.
Hopefully this helps. Ask away if you have doubts.
For Swift 3:
First you need to set allowance to receive User's GPS in the info.plist.
Set: NSLocationWhenInUseUsageDescription with a random String.
And/or: NSLocationAlwaysUsageDescription with a random String.
Then:
import UIKit
import MapKit
class ViewController: UIViewController {
var locManager = CLLocationManager()
var currentLocation: CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
locManager.requestWhenInUseAuthorization()
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse ||
CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways){
guard let currentLocation = locManager.location else {
return
}
print(currentLocation.coordinate.latitude)
print(currentLocation.coordinate.longitude)
}
}
}
Done.
Despite other advice you should use the CLLocationManagerDelegate to safely retrieve a location (without using it you may get null locations when the location manager doesn't have enough time to update). I strongly recommend wrapping the location manager code within a static shared helper (something along these lines):
class Locator: NSObject, CLLocationManagerDelegate {
enum Result <T> {
case .Success(T)
case .Failure(ErrorType)
}
static let shared: Locator = Locator()
typealias Callback = (Result <Locator>) -> Void
var requests: Array <Callback> = Array <Callback>()
var location: CLLocation? { return sharedLocationManager.location }
lazy var sharedLocationManager: CLLocationManager = {
let newLocationmanager = CLLocationManager()
newLocationmanager.delegate = self
// ...
return newLocationmanager
}()
// MARK: - Authorization
class func authorize() { shared.authorize() }
func authorize() { sharedLocationManager.requestWhenInUseAuthorization() }
// MARK: - Helpers
func locate(callback: Callback) {
self.requests.append(callback)
sharedLocationManager.startUpdatingLocation()
}
func reset() {
self.requests = Array <Callback>()
sharedLocationManager.stopUpdatingLocation()
}
// MARK: - Delegate
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
for request in self.requests { request(.Failure(error)) }
self.reset()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: Array <CLLocation>) {
for request in self.requests { request(.Success(self)) }
self.reset()
}
}
Then in view did load (or anywhere else you need to get the current location) run:
Locator.shared.locate { result in
switch result {
case .Success(locator):
if let location = locator.location { /* ... */ }
case .Failure(error):
/* ... */
}
}
In Swift
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
//Labels outlets
#IBOutlet var localityTxtField: UITextField!
#IBOutlet var postalCodeTxtField: UITextField!
#IBOutlet var aAreaTxtField: UITextField!
#IBOutlet var countryTxtField: UITextField!
let locationManager = CLLocationManager()
//View Didload
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
//Button Location
#IBAction func findMyLocation(_ sender: AnyObject) {
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 {
print("placemarks",placemarks!)
let pm = placemarks?[0]
self.displayLocationInfo(pm)
} else {
print("Problem with the data received from geocoder")
}
})
}
func displayLocationInfo(_ placemark: CLPlacemark?) {
if let containsPlacemark = placemark {
print("your location is:-",containsPlacemark)
//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 : ""
localityTxtField.text = locality
postalCodeTxtField.text = postalCode
aAreaTxtField.text = administrativeArea
countryTxtField.text = country
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error while updating location " + error.localizedDescription)
}
}
In current thread a solution was proposed without delegate but in Xcode 9.1 testing in simulator it did not work, location was nil. This code worked:
import UIKit
import MapKit
class ViewController: UIViewController, CLLocationManagerDelegate {
var locationManager: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
if (CLLocationManager.locationServicesEnabled())
{
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations.last! as CLLocation
/* you can use these values*/
let lat = location.coordinate.latitude
let long = location.coordinate.longitude
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I am junior but I solved it in this way:
I have created extension of my class inherited CLLocationManagerDelegate and following steps:
1.Import CoreLocation to your ViewController
import CoreLocation
2.Then initialize location manager and location variables inside your ViewController.
var locManager = CLLocationManager()
var currentUserLocation: CLLocation!
Inside viewDidLoad() request location init delegate and requestUsageDescription
locManager.requestWhenInUseAuthorization()
locManager.delegate = self
locManager.requestLocation()
Then I have just created extension for my viewController inheriting CLLocationManagerDelegate
extension theNameOfYourViewController: CLLocationManagerDelegate{
func locationManager(_ manager: CLLocationManager, didFailWithError error: Swift.Error) {
print(error)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// .requestLocation will only pass one location to the locations array
// hence we can access it by taking the first element of the array
if let location = locations.first {
print(location.coordinate.latitude)
print(location.coordinate.longitude)
}
}
}
Just remember to change the names according your needs also whenever you need location just use the function request location
locManager.requestLocation()
I agree with Kevin above, but if you're looking for less code for something simpler the following will suffice:
Make sure to use the CLLocationManagerDelegate
Swift 4:
In viewDidLoad you can add the following
locationManager.requestWhenInUseAuthorization()
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse) || (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways) {
currentLocation = locationManager.location
print(currentLocation.coordinate.latitude)
print(currentLocation.coordinate.longitude)
}
}
And for the first request respond once the user gives or denies permission:
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.requestLocation()
currentLocation = locationManager.location
print(currentLocation.coordinate.latitude)
print(currentLocation.coordinate.longitude)
//Process location information and update.
}
Make sure to add the following keys to Info.plist:
Privacy - Location When In Use Usage Description
Privacy - Location Always and When In Use Usage Description
Create User class:
import Foundation
import CoreLocation
import MapKit
class User: NSObject, ObservableObject {
#Published var position = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
let manager = CLLocationManager()
override init() {
super.init()
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.requestLocation()
}
}
extension User: CLLocationManagerDelegate {
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
print("Location services authorization request")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("User location updated")
print("Updated position: \(locations.first?.coordinate.latitude ?? 00)")
if let location = locations.first {
self.position = location.coordinate
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Failed to find user's location: \(error.localizedDescription)")
}
}
Update
Swift 5+
Xcode 13+
Add these permission in info plist
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs your location to show nearby services</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs your location to show nearby services</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs your location to show nearby services</string>
Import this in your view controller
import CoreLocation
in viewDidLoad()
override func viewDidLoad() {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
Create extension like this
extension RegisterViewController : CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("error:: \(error.localizedDescription)")
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.requestLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let locationSafe = locations.last {
locationManager.stopUpdatingLocation()
let latitude = locationSafe.coordinate.latitude
let longitude = locationSafe.coordinate.longitude
self.currentLatitude = latitude
self.currentLongitude = longitude
print(" Lat \(latitude) , Longitude \(longitude)")
}
if locations.first != nil {
print("location:: \(locations[0])")
}
}
}
Run and check this

CLLocationManager delegate methods are not getting called(google maps is integrated)

The delegate Methods of CLLocationManager
didChangeAuthorizationStatus
and
didUpdateToLocation
are not getting called.
Location Always Usage Description key is already added in info.plist and I am getting notification also when i launch app for the first time.
I am able to see the google map, but i am not able to see current location, When i change location, its not getting updated. Basicaaly delegate methods are not getting called.
//code
import UIKit
import GoogleMaps
class ViewController: UIViewController,GMSMapViewDelegate {
#IBOutlet weak var mapViewTest: GMSMapView!
let locationManager = CLLocationManager()
var currentLocation :CLLocation = CLLocation(latitude: 0.0, longitude: 0.0)
var currentLatitude : Double = 0.0
var currentLongitude : Double = 0.0
override func viewDidLoad()
{
super.viewDidLoad()``
locationManager.delegate = self
if (CLLocationManager.locationServicesEnabled())
{
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.allowsBackgroundLocationUpdates = true
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
// Do any additional setup after loading the view, typically from a nib.
}
}
extension ViewController : CLLocationManagerDelegate
{
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus)
{
if status == .authorizedAlways
{
if(CLLocationManager .locationServicesEnabled())
{
locationManager.startUpdatingLocation()
mapViewTest.isMyLocationEnabled = true
mapViewTest.settings.myLocationButton = true
}
}
}
func locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation)
{
mapViewTest.camera = GMSCameraPosition(target: (newLocation.coordinate), zoom: 15, bearing: 0, viewingAngle: 0)
currentLocation = newLocation
currentLatitude = newLocation.coordinate.latitude
currentLongitude = newLocation.coordinate.longitude
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError)
{
print("Errors: " + error.localizedDescription)
}
}
From your code you are working with Swift 3, and in Swift 3 CLLocationManagerDelegate method's signature is changed like this.
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
}
//func locationManager(_ manager: CLLocationManager, didUpdateTo newLocation: CLLocation,
from oldLocation: CLLocation) is deprecated with below one
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
}
Check Apple Documentation on CLLocationManagerDelegate for more details.
After checking your code i found some changes you needs to do as follow,
note : I have only added the code having issue here that of location manager
import UIKit
import CoreLocation
class ViewController: UIViewController {
let locationManager = CLLocationManager()
var currentLocation :CLLocation = CLLocation(latitude: 0.0, longitude: 0.0)
var currentLatitude : Double = 0.0
var currentLongitude : Double = 0.0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
locationManager.delegate = self
if (CLLocationManager.locationServicesEnabled())
{
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.allowsBackgroundLocationUpdates = true
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ViewController : CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
func locationManager(_ manager: CLLocationManager, didFinishDeferredUpdatesWithError error: Error?) {
print("Errors: " + (error?.localizedDescription)!)
}
}
Also add below lines in .plist file if not added,
<key>NSLocationWhenInUseUsageDescription</key>
<string>$(PRODUCT_NAME) location use</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>$(PRODUCT_NAME) always uses location </string>
Add these two properties in your info.plist
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
you need to put the mapview delegate to self.
try this:
override func viewDidLoad() {
super.viewDidLoad()
// User Location Settings
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
// Google Maps Delegate
mapView.delegate = self
}
// View will appear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Google maps settings
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
// Get location if autorized
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse) {
let (latitude, longitude) = self.getLocation()
mapView.camera = GMSCameraPosition.camera(
withLatitude: latitude,
longitude: longitude,
zoom: 14)
}
}
//Get the user location
func getLocation() -> (latitude: CLLocationDegrees, longitude: CLLocationDegrees) {
let latitude = (locationManager.location?.coordinate.latitude)!
let longitude = (locationManager.location?.coordinate.longitude)!
return (latitude, longitude)
}

Swift + CLLocationManager: How to tell if the user is in a specific city?

I use CLLocationManager to request the user's location. However, if they are outside of New York City, I want to default to certain coordinates. Is there a way to check if they are in a certain city?
import UIKit
import CoreLocation
import GoogleMaps
private let kDefaultLatitude: Double = 40.713
private let kDefaultLongitude: Double = -74.000
private let kDefaultZoomLevel: Float = 16.0
class RootMapViewController: UIViewController {
#IBOutlet weak var mapView: GMSMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
fetchLocation()
}
private func fetchLocation() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
}
// MARK: CLLocationManagerDelegate
extension RootMapViewController: CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locationManager.stopUpdatingLocation()
let userCoordinates = locations[0].coordinate
// How do I check if the user is in NYC?
// if user is in nyc
centerMapOn(userCoordinates)
mapView.myLocationEnabled = true
mapView.settings.myLocationButton = true
// else default to Times Square
}
}
You can use reverse geocoding. For example you can place:
geocoder:CLGeocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(locations[0],completionHandler{
if error == nil && placemarks.count > 0 {
let location = placemarks[0] as CLPlacemark
print(location.locality)
})
in func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation])

How to get current longitude and latitude using CLLocationManager-Swift

I want to get the current longitude and latitude of a location using Swift and display them via labels. I tried to do this but nothing displays on the labels.
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate{
#IBOutlet weak var longitude: UILabel!
#IBOutlet weak var latitude: UILabel!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
if (CLLocationManager.locationServicesEnabled()) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
} else {
println("Location services are not enabled");
}
}
// MARK: - CoreLocation Delegate Methods
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
locationManager.stopUpdatingLocation()
removeLoadingView()
if (error) != nil {
print(error)
}
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var locationArray = locations as NSArray
var locationObj = locationArray.lastObject as CLLocation
var coord = locationObj.coordinate
longitude.text = coord.longitude
latitude.text = coord.latitude
longitude.text = "\(coord.longitude)"
latitude.text = "\(coord.latitude)"
}
}
IMHO, you are over complicating your code when the solution you are looking is pretty simple.
I have done it by using the following code:
First create an instance of CLLocationManager and Request Authorization
var locManager = CLLocationManager()
locManager.requestWhenInUseAuthorization()
then check if the user allowed authorization.
var currentLocation: CLLocation!
if
CLLocationManager.authorizationStatus() == .authorizedWhenInUse ||
CLLocationManager.authorizationStatus() == .authorizedAlways
{
currentLocation = locManager.location
}
to use it just do this
label1.text = "\(currentLocation.coordinate.longitude)"
label2.text = "\(currentLocation.coordinate.latitude)"
Your idea of setting them to the label.text is correct, however the only reason I can think of is that the user is not giving you permission and that is why your current Location data will be nil.
However you would need to debug and tell us that.
Also the CLLocationManagerDelegate is not necessary.
Hopefully this helps. Ask away if you have doubts.
For Swift 3:
First you need to set allowance to receive User's GPS in the info.plist.
Set: NSLocationWhenInUseUsageDescription with a random String.
And/or: NSLocationAlwaysUsageDescription with a random String.
Then:
import UIKit
import MapKit
class ViewController: UIViewController {
var locManager = CLLocationManager()
var currentLocation: CLLocation!
override func viewDidLoad() {
super.viewDidLoad()
locManager.requestWhenInUseAuthorization()
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse ||
CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways){
guard let currentLocation = locManager.location else {
return
}
print(currentLocation.coordinate.latitude)
print(currentLocation.coordinate.longitude)
}
}
}
Done.
Despite other advice you should use the CLLocationManagerDelegate to safely retrieve a location (without using it you may get null locations when the location manager doesn't have enough time to update). I strongly recommend wrapping the location manager code within a static shared helper (something along these lines):
class Locator: NSObject, CLLocationManagerDelegate {
enum Result <T> {
case .Success(T)
case .Failure(ErrorType)
}
static let shared: Locator = Locator()
typealias Callback = (Result <Locator>) -> Void
var requests: Array <Callback> = Array <Callback>()
var location: CLLocation? { return sharedLocationManager.location }
lazy var sharedLocationManager: CLLocationManager = {
let newLocationmanager = CLLocationManager()
newLocationmanager.delegate = self
// ...
return newLocationmanager
}()
// MARK: - Authorization
class func authorize() { shared.authorize() }
func authorize() { sharedLocationManager.requestWhenInUseAuthorization() }
// MARK: - Helpers
func locate(callback: Callback) {
self.requests.append(callback)
sharedLocationManager.startUpdatingLocation()
}
func reset() {
self.requests = Array <Callback>()
sharedLocationManager.stopUpdatingLocation()
}
// MARK: - Delegate
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
for request in self.requests { request(.Failure(error)) }
self.reset()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: Array <CLLocation>) {
for request in self.requests { request(.Success(self)) }
self.reset()
}
}
Then in view did load (or anywhere else you need to get the current location) run:
Locator.shared.locate { result in
switch result {
case .Success(locator):
if let location = locator.location { /* ... */ }
case .Failure(error):
/* ... */
}
}
In Swift
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
//Labels outlets
#IBOutlet var localityTxtField: UITextField!
#IBOutlet var postalCodeTxtField: UITextField!
#IBOutlet var aAreaTxtField: UITextField!
#IBOutlet var countryTxtField: UITextField!
let locationManager = CLLocationManager()
//View Didload
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
//Button Location
#IBAction func findMyLocation(_ sender: AnyObject) {
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 {
print("placemarks",placemarks!)
let pm = placemarks?[0]
self.displayLocationInfo(pm)
} else {
print("Problem with the data received from geocoder")
}
})
}
func displayLocationInfo(_ placemark: CLPlacemark?) {
if let containsPlacemark = placemark {
print("your location is:-",containsPlacemark)
//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 : ""
localityTxtField.text = locality
postalCodeTxtField.text = postalCode
aAreaTxtField.text = administrativeArea
countryTxtField.text = country
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error while updating location " + error.localizedDescription)
}
}
In current thread a solution was proposed without delegate but in Xcode 9.1 testing in simulator it did not work, location was nil. This code worked:
import UIKit
import MapKit
class ViewController: UIViewController, CLLocationManagerDelegate {
var locationManager: CLLocationManager!
override func viewDidLoad() {
super.viewDidLoad()
if (CLLocationManager.locationServicesEnabled())
{
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
let location = locations.last! as CLLocation
/* you can use these values*/
let lat = location.coordinate.latitude
let long = location.coordinate.longitude
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I am junior but I solved it in this way:
I have created extension of my class inherited CLLocationManagerDelegate and following steps:
1.Import CoreLocation to your ViewController
import CoreLocation
2.Then initialize location manager and location variables inside your ViewController.
var locManager = CLLocationManager()
var currentUserLocation: CLLocation!
Inside viewDidLoad() request location init delegate and requestUsageDescription
locManager.requestWhenInUseAuthorization()
locManager.delegate = self
locManager.requestLocation()
Then I have just created extension for my viewController inheriting CLLocationManagerDelegate
extension theNameOfYourViewController: CLLocationManagerDelegate{
func locationManager(_ manager: CLLocationManager, didFailWithError error: Swift.Error) {
print(error)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// .requestLocation will only pass one location to the locations array
// hence we can access it by taking the first element of the array
if let location = locations.first {
print(location.coordinate.latitude)
print(location.coordinate.longitude)
}
}
}
Just remember to change the names according your needs also whenever you need location just use the function request location
locManager.requestLocation()
I agree with Kevin above, but if you're looking for less code for something simpler the following will suffice:
Make sure to use the CLLocationManagerDelegate
Swift 4:
In viewDidLoad you can add the following
locationManager.requestWhenInUseAuthorization()
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse) || (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways) {
currentLocation = locationManager.location
print(currentLocation.coordinate.latitude)
print(currentLocation.coordinate.longitude)
}
}
And for the first request respond once the user gives or denies permission:
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.requestLocation()
currentLocation = locationManager.location
print(currentLocation.coordinate.latitude)
print(currentLocation.coordinate.longitude)
//Process location information and update.
}
Make sure to add the following keys to Info.plist:
Privacy - Location When In Use Usage Description
Privacy - Location Always and When In Use Usage Description
Create User class:
import Foundation
import CoreLocation
import MapKit
class User: NSObject, ObservableObject {
#Published var position = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
let manager = CLLocationManager()
override init() {
super.init()
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.requestLocation()
}
}
extension User: CLLocationManagerDelegate {
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
print("Location services authorization request")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("User location updated")
print("Updated position: \(locations.first?.coordinate.latitude ?? 00)")
if let location = locations.first {
self.position = location.coordinate
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Failed to find user's location: \(error.localizedDescription)")
}
}
Update
Swift 5+
Xcode 13+
Add these permission in info plist
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs your location to show nearby services</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs your location to show nearby services</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs your location to show nearby services</string>
Import this in your view controller
import CoreLocation
in viewDidLoad()
override func viewDidLoad() {
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
Create extension like this
extension RegisterViewController : CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("error:: \(error.localizedDescription)")
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.requestLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let locationSafe = locations.last {
locationManager.stopUpdatingLocation()
let latitude = locationSafe.coordinate.latitude
let longitude = locationSafe.coordinate.longitude
self.currentLatitude = latitude
self.currentLongitude = longitude
print(" Lat \(latitude) , Longitude \(longitude)")
}
if locations.first != nil {
print("location:: \(locations[0])")
}
}
}
Run and check this

Resources