My code is included below. It's a class intended to return the current location coordinates. The only output I get is "before after before after" - the print lines surrounding requestAlwaysAuthorization. And then the App crashes. The request dialog sometimes shows briefly, sometimes for a few seconds. On few occasions I even get to press "OK". The App always crashes. I've had this problem in xCode 7.0 and now xCode 7.0.1, iOS 9.0 in both cases. I have searched StackOverflow high and low, and most questions on this topic are for earlier versions of both xCode and iOS, and none of the posted solutions helped in my case. Hence, this question. I've also found YouTube tutorials that do basically what I'm doing, but no joy. I also have NSLocationAlwaysUsageDescription in my plist, and I have Privacy - Location Usage Description and NSLocationWhenInUseUsageDescription for good measure. I have also tried sending a location from xCode via the Product\Scheme menu, and I have also tried using the simulator's Debug\Location. I've tried a few different location options for each. The simulator's Map app always seems to work. And Apple's LocateMe (written in Objective C) also works. Swift 2 (my code below) fails.
import CoreLocation
class TheCurrentLocation: NSObject, CLLocationManagerDelegate {
var locationManager: CLLocationManager!
var latitude: Double = 0
var longitude: Double = 0
var locationStatus: NSString = "Not Started"
func initialize() {
self.locationManager = CLLocationManager()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
print("before")
self.locationManager.requestAlwaysAuthorization()
// self.locationManager.requestWhenInUseAuthorization()
print("after")
} // END: initialize()
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
self.locationManager.stopUpdatingLocation()
print("ERRORS: " + error.localizedDescription )
} // END: locationManager delegate didFailWithError
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last
let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
self.locationManager.stopUpdatingLocation()
print ("ta da")
self.latitude = center.latitude
self.longitude = center.longitude
} // END: locationManager delegate didUpdateLocations
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
var isAllowed = false
switch status {
case CLAuthorizationStatus.Restricted:
locationStatus = "Restricted Access to Location"
case CLAuthorizationStatus.Denied:
locationStatus = "User Denied Access to Location"
case CLAuthorizationStatus.NotDetermined:
locationStatus = "Location Status Not Determined"
default:
locationStatus = "Allowed Access to Location"
isAllowed = true
} // END switch status
if (isAllowed == true) {
NSLog(String(locationStatus))
self.locationManager.startUpdatingLocation()
} else {
NSLog(String(locationStatus))
}
} // END: locationManager delegate didChangeAuthorizationStatus
} // END: theCurrentLocation
I think I know what may be causing it. You cant have both NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription. Only one of them. I think I ran into this problem before. Try to remove one of them and see if that fixes it.
Related
How do I track in the background in iOS a user walking in Apple MapKit or Google Maps Platform for iOS and calculate the distance traveled so far using Swift and Xcode?
I have tried using MapKit. The problem I get is that when the iOS device is locked, Core Location stops tracking the device. When the device is unlocked and the app comes to the foreground, Core Location starts tracking again and draws a straight line from the last point tracked to the point tracked when the app comes to the foreground, thus skipping all the time that the device is locked. If Google Maps Platform works better for this purpose, I am willing to use it.
Following is the relevant code:
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.allowsBackgroundLocationUpdates = true
if #available(iOS 11.0, *) {
locationManager.showsBackgroundLocationIndicator = true
}
let status = CLLocationManager.authorizationStatus()
if status == .authorizedAlways || status == .authorizedWhenInUse || status == .restricted {
locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges()
} else {
locationManager.requestAlwaysAuthorization()
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedAlways || status == .authorizedWhenInUse || status == .restricted {
locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges()
} else {
locationManager.requestAlwaysAuthorization()
}
}
Did you add in your .plist,
Privacy - Location Always and When In Use Usage Description and
Privacy - Location When In Use Usage Description?
Then as you are probably conforming CLLocationManagerDelegate in the method didUpdateLocations you should be able to perform your code.
My mistake was that I was creating the line with the user location from the map instead of the user location passed to the didUpdateLocations method. The userLocation property on the map does not update in the background, whereas the userLocation property of CLLocationManager does update in the background.
Here is the corrected code, with a comment on the line that I corrected:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("didUpdateLocations", locations)
if let userLocation = locations.first, polyline != nil {
let userLocationMapPoint = MKMapPoint(userLocation.coordinate)
let lastUserLocationMapPoint = MKMapPoint(route.last!)
totalDistance += userLocationMapPoint.distance(to: lastUserLocationMapPoint)
mapView.removeOverlay(polyline)
route.append(userLocation.coordinate) // changed from route.append(mapView.userLocation.coordinate)
polyline = MKPolyline(coordinates: route, count: route.count)
mapView.addOverlay(polyline)
}
print("latitude:", mapView.userLocation.coordinate.latitude, "longitude:", mapView.userLocation.coordinate.longitude)
}
SITUATION:
I followed the following tutorial:
https://www.raywenderlich.com/95014/geofencing-ios-swift
PROBLEM:
The following functions never get triggered:
AppDelegate.swift
func locationManager(manager: CLLocationManager, didEnterRegion region: CLRegion) {
if region is CLCircularRegion {
handleRegionEvent(region)
}
}
func locationManager(manager: CLLocationManager, didExitRegion region: CLRegion) {
if region is CLCircularRegion {
handleRegionEvent(region)
}
}
func handleRegionEvent(region: CLRegion!) {
print("Geofence triggered!")
// Show an alert if application is active
if UIApplication.sharedApplication().applicationState == .Active {
if let message = notefromRegionIdentifier(region.identifier) {
if let viewController = window?.rootViewController {
showSimpleAlertWithTitle("Congratulations", message: "You just found: " + message , viewController: viewController)
}
}
} else {
// Otherwise present a local notification
let notification = UILocalNotification()
notification.alertBody = "You just found: " + notefromRegionIdentifier(region.identifier)!
notification.soundName = "Default";
UIApplication.sharedApplication().presentLocalNotificationNow(notification)
}
}
QUESTION:
The tutorial was written for iOS 8. I am currently on iOS 9.3. What caused this issue in your opinion and how do I fix it ?
You didn't show the code that you use to set up CL - which is probably where your problem lies.
Did you edit info.plist?
Are you requesting permission?
Did you call one of the start functions on the CL manager?
Make sure of two things :-
1.) You have added These to your viewDidLoad() :-
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestWhenInUseAuthorization()
locationManager.startMonitoringSignificantLocationChanges()
locationManager.startUpdatingLocation()
Another alternative to requestWhenInUseAuthorization() and startUpdatingLocation() initialisation in specific to Swift 2.2, since in Swift 2.2 the string literals for selectors is deprecated, and instead there this new operator #selector that you need to be using. :-
you can also use :-
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.startMonitoringSignificantLocationChanges()
if locationManager.respondsToSelector(#selector(locationManager.requestWhenInUseAuthorization)) {
locationManager.requestWhenInUseAuthorization()
}
else {
locationManager.startUpdatingLocation()
}
//Prefer the FIRST ONE.
2.) You have updated your info.plist with :-
NSLocationAlwaysUsageDescription : String :-> I need location.
NSLocationWhenInUseUsageDescription: String :-> I need location.
privacy - location usage description: String :-> I need location.
Edit I need location according to the app's need
PS :- If it still not calls your locationManager functions
Simulator :- look for location settings of your app in your simulator settings.
Device: - Go in settings > Privacy > Location services > Your app > Always.
you also might find this explanation useful : - https://stackoverflow.com/a/26090094/6297658
initialize your location manager in app delegate on did finish launching
Yeah ! My CoreLocation working now ! So i have a problem to store latitude and longitude and stopping locating.
I have a Sign In page (my MainVC)
before user sign in i need to get and store current device location and stop locating (now it's working with help of Alexander see under).
in my MainVC.swift i have this 2 global variables
var locationManager:CLLocationManager!
var myLocations = [CLLocation]()
in my viewDidLoad() i have this :
//Setup our Location Manager
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
For operate locationManager i have this 2 functions :
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("locations = \(locations)")
errorMsgIfInternetNotAvailable.text = "GPS success"
myLocations.append(locations.last!)
locationManager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
errorMsgIfInternetNotAvailable.text = "Error while updating location " + error.localizedDescription
}
When i build my app, all working, if not autorise my device to get GPS informations i have the locationManager function error working.
If i autorise it
in console, the location update every second and don't stop it with the .stopUpdatingLocation() command
First, it is always good to provide the build error output. It's hard enough to guess which error you're getting.
It seems like you defined, but not initialized myLocations property. You have to do it like this:
var myLocations = [CLLocation]()
Notice the brackets () in initialization.
And then you have to add object to your array:
myLocations.append(locations.last)
or, if you want to store only one object in your array, do it like:
myLocations = [locations.last]
When implementing a CLLocationManager I came across a problem. Sometimes, when launching the app in the iPhone Simulator, it just doesn't fetch the current locations. But when I restart the app or it then suddenly works after 1-3 restarts. Restarting Xcode works too... Here's my code:
private var lat = 0.0;
private var long = 0.0;
private var didFindLocation = false
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if (status == .AuthorizedAlways) || (status == .AuthorizedWhenInUse) {
didFindLocation = false
locationManager.startUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last! as CLLocation
self.lat = location.coordinate.latitude
self.long = location.coordinate.longitude
if !didFindLocation {
print("\(self.lat) - \(self.long)")
didFindLocation = true
locationManager.stopUpdatingLocation()
}
}
As you can see, I've created a bool didFindLocation that allows me to fetch the location only once. I've put some breakpoints to see what's going on, and when the location doesn't get fetched, func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) doesn't even get called.
Thanks
iOS Simulator do not contains sensors like accelerometer, gyrometer etc, so we should not expect to work them on simulators. Besides this, for fetching a location, perhaps, it is using your system's internet.
So for a fair result it is advisable to use a real device for such cases.
From coding perspective, you can check for locationServicesEnabled property of CCLocationManager to see if this service is enabled or not.
This can happen on a simulator, but do add this method so that you get an error message of why it´s not working even though it´s on a simulator
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print(error.localizedDescription)
}
When you check location in simulator then first you need to point your simulator at some location.
Why?
Because simulator is not real time device first you need to point it at some position or location.
How you can do it?
There are two ways to do it.
1) select the simulator -> Goto debug -> select location -> select apple location.
Your location method called
Noto : if you select custom location from menu and enter your custom location then you need to restart your simulator (some time multiple times)
2) Goto xcode -> run your project -> in debug panel you find one location icon -> click on it -> it display location name -> select any place name
Note : Not necessary it worked all the times.
my viewController looks like following
import CoreLocation
class MyViewController: UIViewController {
let locationManager = CLLocationManager()
override func viewDidAppear(animated: Bool) {
if #available(iOS 8.0, *) {
self.locationManager.requestWhenInUseAuthorization()
} else {
// Fallback on earlier versions
}
if (CLLocationManager.locationServicesEnabled()) {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
let lon = locationManager.location!.coordinate.longitude
let lat = locationManager.location!.coordinate.latitude
print("lat = \(lat) and long = \(lon)")
}
}
}
// MARK: - CLLocationManagerDelegate
extension MyViewController : CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.locationManager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("\(error.localizedDescription)")
}
}
When I execute my program, it prompts a message like
Allow application to access your location while you use the app?
But the Don't Allow and Allow buttons are disabled.
Can someone guide me on where I am going wrong and what I should be doing.
I can manually allow my application to access location services by going to settings. But I would like to know why the buttons are disabled and what should I do to enable it.
note: I have added NSLocationWhenInUseUsageDescription to my info.plist file.
Thanks.
Dt: 29Oct2015
EDIT:
Uninstalled the app and installed again and tried. Now I am able to access the buttons.
Also, I noticed that sometimes the screen goes unresponsive i.e., screen cannot take any input from user. I noticed it today with a textbox. I am not able to get the cursor to the text box.
Is it something to do with IOS update? is anyone else experiencing this type of weird behaviour. Is there any workaround for the same?
Any help is highly appreciated.
Thanks.