31

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

Sean Fitz
  • 335
  • 1
  • 3
  • 8

13 Answers13

70

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.

pkamb
  • 33,281
  • 23
  • 160
  • 191
danilopez.dev
  • 816
  • 1
  • 7
  • 5
22

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)")
        }
}
}
pkamb
  • 33,281
  • 23
  • 160
  • 191
jayesh kavathiya
  • 3,531
  • 2
  • 22
  • 25
  • Can you please tell me how I would then use the above in my Controller.swift? – J86 Sep 26 '14 at 21:00
  • you can use any where just write this code in your viewcontroler instead of appdelegate. this will not make any different. just call InitLocationManager() from viewdidload() other all thing will be same... – jayesh kavathiya Sep 29 '14 at 14:55
  • I tried, but did not work for me, I posted a question about it [here](http://bit.ly/1DUgJc5). – J86 Sep 30 '14 at 07:38
  • This was the answer for me. More info at https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html – mire Oct 09 '15 at 19:25
  • This code works but only the first time, once the user accepts the request the locationManager.startUpdatingLocation() never gets called again on subsequent app runs. – Travis M. Aug 02 '17 at 21:53
6

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.

Grimxn
  • 22,115
  • 10
  • 72
  • 85
David Berry
  • 40,941
  • 12
  • 84
  • 95
  • I am sorry, but what do you mean by "enabled it manually" in this case? @David – Sean Fitz Jun 05 '14 at 17:41
  • @SeanFitz Go into the Settings>Privacy>Location and make sure your app is enabled. – David Berry Jun 05 '14 at 17:44
  • @SeanFitz If you're using it on a device it seems to work fine as long as you don't try to use the deprecated method. – David Berry Jun 05 '14 at 17:45
  • I built the code on my device, as you saw above, with the new method as you showed me, but the locationManager() never gets called from the manager.startUpdatingLocation. It just does absolutely nothing. @David – Sean Fitz Jun 05 '14 at 17:51
  • @SeanFitz On a live device, or on the simulator? Go into Settings>Privacy>Location and make sure your application is enabled. – David Berry Jun 05 '14 at 17:58
  • Oh, also you're declaring the manager as local to the function, so it's going to be discarded as soon as the function ends. Remember it works asynchronously so you'll need to move the declaration out to the class scope. – David Berry Jun 05 '14 at 17:59
  • I can't believe I would make the local definition mistake! But alas, my code is identical to yours, the location on the app on the device is set to always, yet the 'locationManager' is never called @David – Sean Fitz Jun 05 '14 at 18:36
  • Do you think there could be any issue with the fact that I am using the beta on the beta of Yosemite and building to iOS8 on my iPhone running a beta? lol @David – Sean Fitz Jun 05 '14 at 18:58
  • @seanfitz edit your post to add the actual code then. What you have there is definitely using a local variable and still using a deprecated delegate method, which isn't supported in swift. – David Berry Jun 05 '14 at 19:39
  • Bizarre question maybe, but have you verified that your ViewController is being used? – David Berry Jun 05 '14 at 19:50
  • Anytime I put any code in the view controller class, everything seems to work fine, except that method is not being called the way it should @David – Sean Fitz Jun 05 '14 at 20:08
  • @SeanFitz I don't know either, if I copy exactly the same code into `AppDelegate` it works. Put it in a `ViewController` and it doesn't. – David Berry Jun 05 '14 at 20:23
  • @SeanFitz And if I change the variable name to `location` it works, try that. – David Berry Jun 05 '14 at 20:25
  • @SeanFitz At least on iOS 7 simulator and device that works, Still can't get it to work on iOS 8 simulator (and way too early to put it on a device) – David Berry Jun 05 '14 at 20:27
  • The variable name change to location didn't work for me, and neither did making a new class and view controller. I am going to chalk this up to a bug for now and revisit later. Thanks for taking the time. @David – Sean Fitz Jun 05 '14 at 20:41
  • Yeah, I was getting spotty results as well. May well be a bug somewhere. Good luck :) – David Berry Jun 05 '14 at 20:44
3

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.

salty
  • 61
  • 3
2

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.

pkamb
  • 33,281
  • 23
  • 160
  • 191
Alvin George
  • 14,148
  • 92
  • 64
2

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

}
Jarda Pavlíček
  • 1,636
  • 17
  • 16
2

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


}
Steffen
  • 161
  • 1
  • 5
1

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

Vassily
  • 899
  • 2
  • 8
  • 19
0

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
Mitul Marsoniya
  • 5,272
  • 4
  • 33
  • 57
0

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 !

Naishta
  • 11,885
  • 4
  • 72
  • 54
  • I just did follow and add corelocation.framework, but it didn't work for me. – early Apr 27 '16 at 16:26
  • not sure if its a feature of a specific version of XCode, but i am using this to track default location of the user, for a weather app – Naishta Apr 27 '16 at 16:39
0

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

    }
Gary Davies
  • 920
  • 15
  • 12
0

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)")
    }
}
Rajesh Loganathan
  • 11,129
  • 4
  • 78
  • 90
0

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

        }


    }
Talha Rasool
  • 1,126
  • 14
  • 12