45

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)"
    }
}
Quick learner
  • 10,632
  • 4
  • 45
  • 55
Beginner
  • 465
  • 1
  • 4
  • 5
  • 1
    possible duplicate of [CLLocation Manager in Swift to get Location of User](http://stackoverflow.com/questions/24063798/cllocation-manager-in-swift-to-get-location-of-user) – Nate Cook Nov 04 '14 at 17:55

9 Answers9

86

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.

regina_fallangi
  • 2,080
  • 2
  • 18
  • 38
S.H.
  • 2,833
  • 3
  • 26
  • 28
  • 1
    Where is your second code block supposed to go? In a delegate method? – Chris Harrison Feb 22 '15 at 08:38
  • 1
    i usually put it in "viewdidload" or "awakefromnib" but it can be used anywhere. it has no limitation as long as is asked the user permission to use their location and that he/she grants it. – S.H. Feb 22 '15 at 12:57
  • 3
    I disagree with this advice. The code above isn't even starting the location manager - it's just making sure it's authorized. The location manager's current location reading is likely to be old and grossly inaccurate. When you activate the location manager and ask for location updates the first few location results tend to be very bad. You really do need to set up a delegate, start location updates, and check the accuracy readings on the location updates you get until they settle down. – Duncan C Jan 30 '16 at 20:44
  • 1
    @DuncanC be that as it may. Respecting your opinion, this code worked for me, I have it in production for an app I created for a Client. And more direct to the point, the OP asked for "Current Long Lat for a Location using Swift." Because apparently it was not working for him. This made it work apparently, like it did for me, thus answering his question. If you want to talk LocationManager Accuracy that my friend is another topic by itself. – S.H. Jan 31 '16 at 00:52
  • Did not work for me when testing in xcode 9.1, in simulator, without delegate location was nil. – Madis Maenni Dec 31 '17 at 08:11
  • "import CoreLocation" is missing for importing all – Venkatesh Jun 12 '18 at 04:40
  • @MadisMaenni I have the same problem with you. Is it still stuck or already get new solution? – Marfin. F Sep 07 '18 at 06:04
36

For Swift 3:

First you need to set allowance to receive User's GPS in the info.plist.

enter image description here

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.

Mr_Nizzle
  • 6,644
  • 12
  • 55
  • 85
David Seek
  • 16,783
  • 19
  • 105
  • 136
16

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):
    /* ... */
  }
}
Kevin Sylvestre
  • 37,288
  • 33
  • 152
  • 232
  • I'm getting an error: "Generic type 'Result' nested in type 'Locator' is not allowed". I've never seen this error in Swift. Any suggestions on a fix? – Clay Ellis May 23 '16 at 15:39
  • @ClayEllis do enum Result declaration out of class and it will work. – yogs Oct 24 '16 at 07:55
  • used as per instruction getting an error like: Use of unresolved identifier 'locator'. into UIViewController. – Ankit Vyas Feb 21 '18 at 10:51
  • 1
    Not working in Swift 4 and Xcode 10, please check once and edit your answer. – nitin.agam Oct 21 '18 at 15:16
  • Try adding let before locator and error. I hope it helps. Locator.shared.locate { (result) in switch result { case .success(let locator): break case .failure(let err): break } } – Usman Sep 30 '21 at 11:32
3

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)
    }
}
Sai kumar Reddy
  • 1,751
  • 20
  • 23
3

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.
}


}
kalpesh
  • 1,285
  • 1
  • 17
  • 30
Madis Maenni
  • 508
  • 4
  • 4
1

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!
  1. Inside viewDidLoad() request location init delegate and requestUsageDescription

     locManager.requestWhenInUseAuthorization()
     locManager.delegate = self
     locManager.requestLocation()
    
  2. 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()
Jasur S.
  • 31
  • 4
0

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.

    }
Sam Bing
  • 2,794
  • 1
  • 19
  • 29
0

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)")
    }

    
}
LineDrop
  • 461
  • 4
  • 5
0

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>

enter image description here

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

enter image description here

Quick learner
  • 10,632
  • 4
  • 45
  • 55