The code for my CLLocationManagerDelegate was working fine before the last iOS update, however now it is crashing with the error 'NSInternalInconsistencyException', reason: 'Delegate must respond to locationManager:didUpdateLocations:'
This is the code for my delegate (note: start() is called from my ViewController):
class Location: NSObject, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
func start() {
locationManager.delegate = self
if locationManager.responds(to: #selector(CLLocationManager.requestAlwaysAuthorization)) {
locationManager.requestAlwaysAuthorization()
} else {
startLocationUpdates()
}
}
func startLocationUpdates() {
locationManager.allowsBackgroundLocationUpdates = true
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.activityType = .automotiveNavigation
locationManager.startUpdatingLocation()
locationManager.requestLocation()
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse || status == .authorizedAlways {
startLocationUpdates()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// snip
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
// snip
}
}
Not sure how you're initializing this class, setting delegates, etc., but this is the UserLocation class that I use. Compare it to yours.
import Foundation
import CoreLocation
var userLocation: UserLocation?
class UserLocation: NSObject, CLLocationManagerDelegate {
var launchLocationSet = false
override init() {
super.init()
setupLocationManager()
}
public var currentLocation: CLLocation!
private var locationManager = CLLocationManager()
private func setupLocationManager() {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
locationManager.delegate = self
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
currentLocation = locations.last! as CLLocation!
if !launchLocationSet {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "updateLocation"), object: nil, userInfo: nil)
launchLocationSet = true
}
}
func currentLatitude() -> CLLocationDegrees {
return currentLocation.coordinate.latitude
}
func currentLongitude() -> CLLocationDegrees {
return currentLocation.coordinate.longitude
}
}
I have a project in swift 2.When I launch the app first time there are three different type of permissions popup (Push notification, Location, Photo) that appear on the splash screen.I have add the Permission for location and photos in info.plist
The problem is when the app lunched the one(location) popup appear and disappear without any click then other(photos) popup appear and disappear after few seconds without any click.After few seconds the popup appear one by one and now the popup are display on the screen until I click any one option.
I want to display the permission popup only once when user tap on the button.I have searched about it but all the solutions that I found are in latest version of swift. Any suggestion regarding this is appreciated.
import CoreLocation
private var locationManager: CLLocationManager!
private var locationHandler: ((location: CLLocation?,error: NSError?) -> Void)?
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
func requestCurrentLocation(completionHandler:((location: CLLocation?,error: NSError?)->Void)!) {
locationHandler = completionHandler
if #available(iOS 9.0, *) {
locationManager.requestLocation()
} else {
locationManager.startUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let locationHandler = locationHandler, let location = locations.first {
locationHandler(location: location, error: nil)
self.locationHandler = nil
locationManager.stopUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
if let locationHandler = locationHandler {
locationHandler(location: nil, error: error)
self.locationHandler = nil
}
}
The key is CLLocationManager always alive
import CoreLocation
class LocationManager: NSObject {
static let shared = LocationManager()
private lazy var locationManager: CLLocationManager = {
let manager = CLLocationManager()
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
return manager
}()
private override init() {
super.init()
}
func askForPermission() {
locationManager.requestWhenInUseAuthorization()
}
func requestLocation() {
locationManager.requestLocation()
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
// do something
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// do something
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
// do something
}
}
In iOS geolocation, the code works if the locationManager is declared and initialized separately, however it does not work if it is declared and initialized at the same time. Why is it so? The following is the working code sample:-
var locationManager : CLLocationManager!
func initLocManager() {
locationManager=CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.activityType = .automotiveNavigation
locationManager.distanceFilter = 10.0
}
func retrieveLocation(){
initLocManager()
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
for location in locations {
print("Long \(location.coordinate.longitude)")
print("Lati \(location.coordinate.latitude)")
}
}
whereas the following code does not work:-
var locationManager = CLLocationManager()
func initLocManager() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.activityType = .automotiveNavigation
locationManager.distanceFilter = 10.0
}
func retrieveLocation(){
initLocManager()
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
for location in locations {
print("Long \(location.coordinate.longitude)")
print("Lati \(location.coordinate.latitude)")
}
}
Another way:
Create new file with this code:
import UIKit
import CoreLocation
class LocationManager: CLLocationManager, CLLocationManagerDelegate {
static let shared = LocationManager()
public var currentLocation = CLLocation() {
didSet {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: LocationManager.LocationUpdatedNotification), object: self, userInfo: nil)
}
}
static let LocationUpdatedNotification: String = "LocationUpdate"
private override init() {
super.init()
self.delegate = self
self.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
self.activityType = .automotiveNavigation
self.distanceFilter = 10.0
self.requestAlwaysAuthorization()
self.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lastLocation = locations.last {
currentLocation = lastLocation
}
}
}
In AppDelegate.swift
_ = LocationManager.shared // Add this line to func didFinishLaunchingWithOptions
Now you can get current user location using this code:
LocationManager.shared.currentLocation
Also you can subscribe for LocationUpdate notification anywhere in your project.
I have this code in my ViewController:
#IBAction func testButton(sender: UIButton) {
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
print("\(CLLocationManager.locationServicesEnabled())")
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
print("Started updating location")
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation: CLLocation = locations[0]
let long = userLocation.coordinate.longitude
let lat = userLocation.coordinate.latitude
print("\(long), \(lat)")
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Location update failed: \(error)")
}
NSLocationWhenInUseUsageDescription in my plist. It should just work according to all information I could find, but it just outputs
true
Started updating location
Is edit 2 of this question true? If so, what would be the best way to move this out of the viewController?
Before startUpdatingLocation(), have to ask for permission using requestWhenInUseAuthorization() first.
So your codes in ViewController will be similar like the following or you can download a sample project here.
class ViewController: UIViewController, CLLocationManagerDelegate {
let locationManager: CLLocationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.testButton()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func testButton() {
if CLLocationManager.authorizationStatus() != .AuthorizedWhenInUse {
self.locationManager.requestWhenInUseAuthorization()
return
}
self.locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation: CLLocation = locations[0]
let long = userLocation.coordinate.longitude
let lat = userLocation.coordinate.latitude
print("\(long), \(lat)")
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Location update failed: \(error)")
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .AuthorizedWhenInUse {
self.testButton()
}
}
}
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
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.
First add this two line in plist file
NSLocationWhenInUseUsageDescription
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)")
}
}
}
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.
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.
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)")
}
}
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)")
}
}
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.
don't forget to add
NSLocationWhenInUseUsageDescription or
NSLocationAlwaysUsageDescription
in your configuration file (target/Info/custom iOS target properties
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
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 !
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)
})
}
}
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)")
}
}
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)
}
}
}