Sean Fitz
Sean Fitz

Reputation: 335

CLLocation Manager in Swift to get Location of User

I am trying to convert an old app in ObjC to Swift as a practice exercise and have ran in to some issues. The way I had it in the old app, it was establishing the CLLocation Manager and then I would use:

manager = [[CLLocationManager alloc]init];
manager.delegate = self;
manager.desiredAccuracy = kCLLocationAccuracyBest;    
[manager startUpdatingLocation]

which would call automatically:

-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
}

and from there I could extract all the information I needed. But in swift, there is no autocompletion of this method and I cannot figure out how to reproduce it. The documentation says that

startUpdatingLocation()

will still be called by the delegate, but it isn't happening.

This is what I have so far:

import UIKit
import corelocation

class ViewController: UIViewController,CLLocationManagerDelegate{

@IBOutlet var gpsResult : UILabel

var manager:CLLocationManager!

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    manager = CLLocationManager()
    manager.delegate = self
    manager.desiredAccuracy = kCLLocationAccuracyBest
    manager.startUpdatingLocation()
}

func locationManager(manager:CLLocationManager, didUpdateLocations locations:AnyObject[]) {
    println("locations = \(locations)")
    gpsResult.text = "success"
}
}

Any help or pointers on where to look would be appreciated. Thanks.

EDIT: Updated from Suggestions, but still not working

EDIT2: Seems to be some bug not allowing the method to work properly in the ViewController

Upvotes: 31

Views: 106218

Answers (13)

danilopez.dev
danilopez.dev

Reputation: 816

You are missing two things. First, you have to ask for permission using requestAlwaysAuthorization or requestWhenInUseAuthorization(). So your viewDidLoad() should be like this:

var locationManager = CLLocationManager()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    locationManager.delegate = self
    locationManager.desiredAccuracy = kCLLocationAccuracyBest
    locationManager.requestAlwaysAuthorization()
    locationManager.startUpdatingLocation()
}

Second, edit your Info.plist as indicated here.

Upvotes: 70

Alvin George
Alvin George

Reputation: 14296

I hope there are two ways.

var locationManager: CLLocationManager = CLLocationManager()
var initialLocation :CLLocation?
var updatedUserLocation :CLLocation?

override func viewDidLoad() {
    super.viewDidLoad() {

    //MapView Location
    locationManager.delegate = self
    locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
    locationManager.requestWhenInUseAuthorization()
    locationManager.startUpdatingLocation()
    locationManager.startUpdatingHeading()
}

Implementing CLLocationManagerDelegate :

//CLLocationManager Delegate
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

    // This only works when user location is updated.
    gpsProviderStatusLabel.changeStatusToOn(gpsProviderStatusLabel)

}

func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {

    //Error indicates GPS permission restricted

    gpsProviderStatusLabel.changeStatusToOff(gpsProviderStatusLabel)

    //Initial Location
    initialLocation = locations.first

    //Getting Updated Location
    updatedUserLocation = locations.last
}

Checking CLLocationDelegate Authorization:

func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {

    //This method does real time status monitoring.

        switch status {
        case .NotDetermined:
          print(".NotDetermined")
          break

        case .AuthorizedAlways:
          print(".AuthorizedAlways")
          gpsProviderStatusLabel.changeStatusToOn(gpsProviderStatusLabel)
          break


        case .Denied:
          print(".Denied")
          gpsProviderStatusLabel.changeStatusToOff(gpsProviderStatusLabel)
          break

        case .AuthorizedWhenInUse:
          print(".AuthorizedWhenInUse")
          gpsProviderStatusLabel.changeStatusToOn(gpsProviderStatusLabel)
          break

        case .Restricted:
          print(".Restricted")
          break

        default:
          print("Unhandled authorization status")
          break

        }
      }

Note: changeStatusToOn or changeStatusToOff is a UILabel Extenion method which makes the Label text On/Off with Green/Red Colors.

Upvotes: 2

jayesh kavathiya
jayesh kavathiya

Reputation: 3541

First add this two line in plist file

  1. NSLocationWhenInUseUsageDescription

  2. NSLocationAlwaysUsageDescription

Then this is class working complete implement this

import UIKit

import CoreLocation

@UIApplicationMain

class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {

var window: UIWindow?
var locationManager: CLLocationManager!
var seenError : Bool = false
var locationFixAchieved : Bool = false
var locationStatus : NSString = "Not Started"

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
    initLocationManager();
    return true
}

// Location Manager helper stuff
func initLocationManager() {
    seenError = false
    locationFixAchieved = false
    locationManager = CLLocationManager()
    locationManager.delegate = self
    locationManager.locationServicesEnabled
    locationManager.desiredAccuracy = kCLLocationAccuracyBest

    locationManager.requestAlwaysAuthorization()
}

// Location Manager Delegate stuff
// If failed
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
    locationManager.stopUpdatingLocation()
    if (error) {
        if (seenError == false) {
            seenError = true
           print(error)
        }
    }
}

func locationManager(manager: CLLocationManager!, didUpdateLocations locations: AnyObject[]!) {
    if (locationFixAchieved == false) {
        locationFixAchieved = true
        var locationArray = locations as NSArray
        var locationObj = locationArray.lastObject as CLLocation
        var coord = locationObj.coordinate

        println(coord.latitude)
        println(coord.longitude)
    }
}

// authorization status
func locationManager(manager: CLLocationManager!,
    didChangeAuthorizationStatus status: CLAuthorizationStatus) {
        var shouldIAllow = false

        switch status {
        case CLAuthorizationStatus.Restricted:
            locationStatus = "Restricted Access to location"
        case CLAuthorizationStatus.Denied:
            locationStatus = "User denied access to location"
        case CLAuthorizationStatus.NotDetermined:
            locationStatus = "Status not determined"
        default:
            locationStatus = "Allowed to location Access"
            shouldIAllow = true
        }
        NSNotificationCenter.defaultCenter().postNotificationName("LabelHasbeenUpdated", object: nil)
        if (shouldIAllow == true) {
            NSLog("Location to Allowed")
            // Start location services
            locationManager.startUpdatingLocation()
        } else {
            NSLog("Denied access: \(locationStatus)")
        }
}
}

Upvotes: 22

Talha Rasool
Talha Rasool

Reputation: 1152

Just call the init(vc : UIViewController).

    import Foundation
    import CoreLocation
    import UIKit


    class LocManager : NSObject{


        var permission : ((Bool?)->())?

        private var locationManager : CLLocationManager!

        init(_ vc : UIViewController) {
            super.init()
            self.locationManager = CLLocationManager()
            self.locationManager.delegate = vc as? CLLocationManagerDelegate
            setUpLocationManagerDelegate()
        }


    }

    extension LocManager : CLLocationManagerDelegate {

        fileprivate func setUpLocationManagerDelegate(){
               locationManager = CLLocationManager()
               locationManager.delegate = self
               locationManager.desiredAccuracy = kCLLocationAccuracyBest
               locationManager.requestAlwaysAuthorization()
           }

        func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

            if let lat  = locations.last?.coordinate.latitude, let long = locations.last?.coordinate.longitude{
                print("\n\nThe current Lat/Long Is Here\n\n")
                let coordinates = CLLocationCoordinate2D(latitude: lat, longitude: long)

            }else{
                print("Unable To Access Locaion")
            }
        }

        func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {

            switch status {
            case .authorizedAlways,.authorizedWhenInUse:
                print("Good to go and use location")
                locationManager.startUpdatingLocation()
                self.callPermisssionCompletion(val: true)

            case .denied:
                print("DENIED to go and use location")
                self.callPermisssionCompletion(val: false)

            case .restricted:
                print("DENIED to go and use location")
                self.callPermisssionCompletion(val: nil)

            case .notDetermined:
                print("DENIED to go and use location")
                self.callPermisssionCompletion(val: nil)

            default:
                print("Unable to read location :\(status)")
            }
        }


        fileprivate func callPermisssionCompletion(val : Bool?){

            guard let comp = self.permission else {
                print("\n\n Unable to  locate completions \n\n")
                return
            }
            if let val =  val{
                comp(val)
            }

        }


    }

Upvotes: 0

Rajesh Loganathan
Rajesh Loganathan

Reputation: 11217

Swift:

Add following in

import CoreLocation
class YourViewController: UIViewController
{
       var locationManager:CLLocationManager!
}


//MARK:- Location Manager
extension YourViewController: CLLocationManagerDelegate {

    func stratLocationManager()
    {
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        self.checkUsersLocationServicesAuthorization()
        locationManager.startUpdatingLocation()

    }

    func checkUsersLocationServicesAuthorization(){
        /// Check if user has authorized Total Plus to use Location Services
        if CLLocationManager.locationServicesEnabled()
        {
            switch CLLocationManager.authorizationStatus()
            {
            case .notDetermined:
                // Request when-in-use authorization initially
                // This is the first and the ONLY time you will be able to ask the user for permission
                self.locationManager.delegate = self
                locationManager.requestWhenInUseAuthorization()
                break

            case .restricted, .denied:
                // Disable location features
                PrintLogs("Location Access Not Available")
                break

            case .authorizedWhenInUse, .authorizedAlways:
                // Enable features that require location services here.
                PrintLogs("Location Access Available")
                break
            }
        }
    }

    func locationManager(_ manager:CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        print("locations = \(locations)")
    }
}

Upvotes: 0

Steffen
Steffen

Reputation: 161

For Swift 3

import UIKit
import CoreLocation

class ViewController: UIViewController,CLLocationManagerDelegate {


    var locationManager:CLLocationManager!

    override func viewDidLoad() {
        super.viewDidLoad()

        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestAlwaysAuthorization()
        locationManager.startUpdatingLocation()

        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func locationManager(_ manager:CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        print("locations = \(locations)")
    }


}

Upvotes: 2

Gary Davies
Gary Davies

Reputation: 960

This will ask for permission and track if given permission else quit with an alert. Stops tracking on back button press.

info.plist

<key>NSLocationAlwaysUsageDescription</key>
<string>Allow tracking while completing a survey</string>

Class:

import UIKit
import CoreLocation    

class LocationViewController: BaseViewController, CLLocationManagerDelegate {

        // MARK: Constants

        private static let enableLocationServices = [
            "title" : "Location",
            "message" : "Enable location services",
            "buttonTitle" : "OK"
        ]

        // MARK: Private variables

        private var manager: CLLocationManager?

        // MARK: UIViewCOntroller methods

        @IBAction func backButtonPressed(sender : UIButton) {
            stopTracking()
            detatchLocationManager()
            dismissViewControllerAnimated(true, completion: nil)
        }

        override func viewDidLoad() {
            super.viewDidLoad()

            attachLocationManager()    
        }

        // Mark: Location

        func locationManager(manager: CLLocationManager,
                             didChangeAuthorizationStatus status: CLAuthorizationStatus)
        {
            if status == .AuthorizedAlways {
                manager.startUpdatingLocation()
            } else if status != .NotDetermined {
                showEnableLocationServicesAlert()
            }
        }

        func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
            for location in locations {
                getDependencyService().getProject().appendLocationTrackingFile(location.timestamp, latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
            }
        }

        // MARK: LocationViewController

        private func attachLocationManager() {
            manager = CLLocationManager()
            manager?.delegate = self
            manager?.desiredAccuracy = kCLLocationAccuracyBest

            if CLLocationManager.authorizationStatus() != .AuthorizedAlways {
                manager?.requestAlwaysAuthorization()
            } else if CLLocationManager.locationServicesEnabled() {
                startTracking()
            }
        }

        private func detatchLocationManager() {
            manager?.stopUpdatingLocation()
            manager?.delegate = nil
            manager = nil
        }

        private func startTracking() {
            manager?.startUpdatingLocation()
        }

        private func stopTracking() {
            manager?.stopUpdatingLocation()
        }

        private func showEnableLocationServicesAlert() {
getDependencyService().getUiHelper().showAlert(FrogFirstSurveyViewController.enableLocationServices, completion: {
                self.dismissViewControllerAnimated(true, completion: nil)
            })
        }

    }

Upvotes: 0

Jarda Pavl&#237;ček
Jarda Pavl&#237;ček

Reputation: 1846

Here is my very simple code that works:

first add Core Location framework in General/Linked Frameworks and Libraries

then add following into Info.plist:

<key>NSLocationWhenInUseUsageDescription</key>
<string>blablabla</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>blablabla</string>

this is my ViewController.swift file:

import UIKit
import CoreLocation

class ViewController: UIViewController, CLLocationManagerDelegate {

    var locationManager:CLLocationManager!

    override func viewDidLoad() {
        super.viewDidLoad()

        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestAlwaysAuthorization()
        locationManager.startUpdatingLocation()
    }


    func locationManager(manager:CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        print("locations = \(locations)")
    }

}

Upvotes: 2

Naishta
Naishta

Reputation: 12343

If you want to get the user location updated by default, without clicking 'Simulate location' everytime, go to

YourProject-->Build Phases-->Link Binary with libraries-->Add corelocation.framework

The location gets updated automatically/by default when you run the app in the simulator. Tested and works in Swift 2 !

Upvotes: 0

Mitul Marsoniya
Mitul Marsoniya

Reputation: 5299

Add bellow 2 property in info.plist

NSLocationWhenInUseUsageDescription : Location information is used for fraud prevention

Privacy - Location Usage Description : Location information is used for fraud prevention

Upvotes: 0

David Berry
David Berry

Reputation: 41226

I'm not sure why, but it seems like startUpdatingLocation isn't presenting the user prompt on the iOS 7 simulator, but when I enabled it manually it worked as expected if I used the newer form of the delegate method:

var manager:CLLocationManager!

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    manager = CLLocationManager()
    manager.delegate = self
    manager.desiredAccuracy = kCLLocationAccuracyBest
    manager.startUpdatingLocation()
}

func locationManager(manager:CLLocationManager, didUpdateLocations locations:[AnyObject]) { // Updated to current array syntax [AnyObject] rather than AnyObject[]
    println("locations = \(locations)")
}

The format you're using has been deprecated since iOS 5 or 6, so apparently it's not supported at all by the swift bridging layers.

Upvotes: 6

Vassily
Vassily

Reputation: 915

don't forget to add NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription in your configuration file (target/Info/custom iOS target properties

Upvotes: 1

salty
salty

Reputation: 61

had the same issue. didUpdateLocations - was not working. Run your app. Go to the Settings page -> Privacy -> Location and turn off Location Services. didFailWithError will catch the error about absent Location Services. Then turn it on. Since that moment didUpdateLocations will catch locations.

Upvotes: 3

Related Questions