core location not asking permission - ios

problem :
For some reason, the app I’m working on is not asking for permission to use location services. This used to work before. Also, I created a new project in which I followed the steps described below EXACTLY in which this worked immediately.
One difference is I know the authorization status in the main app I’m working on is .Denied, it was .NotDetermined in the test project.
I read this answe on stack overflow :
Core Location not requesting user permission
'
Not many people know this, but after you uninstall an application, that application's documents and preferences are still stored on the device, here:
/var/mobile/Library/Safe Harbor/myappidentifier/Container/
in my opinion, this was not a wise move by apple, as that could have security risks, as the one you have explained above.
If an app is re-installed, iOS automatically copies those preferences back into the appropriate folder. That is the cause of the behavior you are seeing.
'
This could explain it because I did reinstall the app but it doesn't say how to solve it.
Thank you
Here’s the relevant code i wrote in the App :
I set the NSLocationAlwaysUsageDescription key in info.plist
I imported core location :
import CoreLocation
implemented the right protocol:
CLLocationManagerDelegate
set this property :
var locationManager = CLLocationManager()
set the delegate in ViewDidLoad
locationManager.delegate = self
and wrote a test function to test this :
if CLLocationManager.locationServicesEnabled() {
println("Location Services Enabled")
if CLLocationManager.authorizationStatus() == .Authorized {
println("Location Services Authorized")
} else if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
println("Location Services Authorized WHEN IN USE")
} else if CLLocationManager.authorizationStatus() == .NotDetermined {
println("Location Services NOT Authorized: NOT DETERMINED")
} else if CLLocationManager.authorizationStatus() == .Restricted {
println("Location Services NOT Authorized: RESTRICTED")
} else if CLLocationManager.authorizationStatus() == .Denied {
println("Location Services NOT Authorized: DENIED")
} else {
println("Location Services NOT Authorized")
locationManager.requestAlwaysAuthorization()
}
} else {
println("!!! Location Services NOT Enabled !!!")
}
Console Output :
Location Services Enabled
Location Services NOT Authorized

You can go into the Settings app, General, and reset Location & Privacy to get the initial permission dialog for your app again. Otherwise, iOS saves the first answer without ever asking again.

Related

Photo Library and Camera are accessible after permission got denied

I'm currently working on an app where I need to access either the photo library or the camera (or even both, it's up to the user).
If I close the app and deny the access to both camera and library for the app.
After restarting my App, it's still possible to access both, I can open and use the camera or even pick a photo from the library.
How is this possible, I thought Apple would restrict the access.
I dont think so. Apple always restrict use of camera or photo library if access is denied. Please use following code to check current status and verify it.
import Photos
if type == .CAMERA{
if AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo) == AVAuthorizationStatus.authorized {
completionHander(true) //Allowed
} else {
//Dont Know
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { (granted: Bool) -> Void in
completionHander(granted)
})
}
}else{
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized:
completionHander(true)//Allowed
break
case .denied, .restricted :
completionHander(false)//Not Allowed
break
case .notDetermined:
//Dont Know
PHPhotoLibrary.requestAuthorization { status in
switch status {
case .authorized:
completionHander(true)
break
case .denied, .restricted:
completionHander(false)
break
case .notDetermined:
completionHander(false)
break
}
}
}
}
Okay I know the answer...
I just didn't get the concept.
If I deny the access to the photoLibrary it is not possible to save images but, if Camera access is granted I can still use the photoLibrary.
If camera access is denied, I still get the alert with the context from the info.plist

Rotate SCNNode to North without permission for location tracking

We would like to rotate a SCNNode so as to match with the North direction.
Arkit allows to align its z-axis with the North but this requires user permission for location tracking, and we don't want to ask for it.
see gravityandheading
As a consequence, we're trying to use the magnetic field property of CMDeviceMotion. But we have no idea how to do that.
There are probably some matrix calculations, but we don't master them for the time being.
see magneticfield
Any help would be highly appreciated! thanks!
You cannot use Location Tracking without user's permission.
Here are some tips on how to setup it properly for AR apps and NON-AR apps:
In iOS 11 and higher here's what you need to do to get location working:
Add a key to your info.plist and request authorisation from the location manager asking it to start. There are two Property List Keys in info.plist for the location authorisation. One or both of these keys is required. In NON-AR apps if neither of the keys are there, you can call startUpdatingLocation method but the location manager won’t actually start. It won’t send a failure message to the delegate either (since it never started, it can’t fail). It will also fail if you add one or both of the keys but forget to explicitly request authorisation.
Use these two Property List Keys in info.plist file. They are used since iOS 11 was released:
<key>NSLocationWhenInUseUsageDescription</key>
<string>App needs an access to your location (in foreground)</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>App needs an access to your location (in background)</string>
And these two Property List Keys were deprecated earlier (DON'T USE THESE KEYS):
<key>NSLocationUsageDescription</key>
<string>App needs an access to your location (in background)</string>
<key>NSLocationAlwaysUsageDescription</key>
<true/>
Here's how your code for AR apps should look like:
let configuration = ARWorldTrackingConfiguration()
func session(_ session: ARSession, didFailWithError error: Error) {
switch error.code {
case 101:
configuration.worldAlignment = .gravity
restartSession()
default:
configuration.worldAlignment = .gravityAndHeading
restartSession()
}
}
func restartSession() {
self.sceneView.session.pause()
self.sceneView.session.run(configuration,
options: [.resetTracking, .removeExistingAnchors])
}
For NON-AR apps use these two instance methods: requestAlwaysAuthorization() (requests permission to use location services whenever the app is running) and requestWhenInUseAuthorization() (requests permission to use location services while the app is in the foreground).
Here's how your code for NON-AR apps should look like:
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
func updateMyLocation() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
if locationManager.respondsToSelector(#selector(CLLocationManager.requestWhenInUseAuthorization)) {
locationManager.requestWhenInUseAuthorization()
} else {
locationManager.startUpdatingLocation()
}
}
}
Also, you can request Always Authorization:
let locationManager = CLLocationManager()
func enableLocationServices() {
locationManager.delegate = self
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
// Request when-in-use authorization initially
locationManager.requestWhenInUseAuthorization()
break
case .restricted, .denied:
// Disable location features
disableMyLocationBasedFeatures()
break
case .authorizedWhenInUse:
// Enable basic location features
enableMyWhenInUseFeatures()
break
case .authorizedAlways:
// Enable any of your app's location features
enableMyAlwaysFeatures()
break
}
}
Hope this helps.

How to know if locationManager.requestAlwaysAuthorization() has already been asked

When requesting user's iOS location permissions, how could I know if locationManager.requestAlwaysAuthorization() has already been asked to the user?
In case the user had .AuthorizedWhenInUse status and the request for always authorization has been denied, the always-auth prompt for the next request won't be shown so I won't get any callback of this request launch.
Any ideas?
You need to check CLLocationManager.authorizationStatus() and only request authorization if the value is .notDetermined, since this is the only case when the authorization prompt will actually be shown.
You can check the Authorization status and compare if it's notDetermined it has not been asked, else - it's been asked.
You can know by using authorizationStatus() like this.
if CLLocationManager.authorizationStatus() == .notDetermined {
print("Not Determined")
}
else if CLLocationManager.authorizationStatus() == .restricted {
print("Restricted")
}
else if CLLocationManager.authorizationStatus() == .denied {
print("Denied")
}
else if CLLocationManager.authorizationStatus() == .authorizedAlways {
print("Always Authorized")
}
else if CLLocationManager.authorizationStatus() == .authorizedWhenInUse {
print("Authorized When Require")
}
If the Dialog appear for 1st time it returns .notDetermined status and if you respond to dialog than it returns status based on your selection like if you allow to access your location always that it returns .authorizedAlways.
After a lot of brainstorming this issue, I've found a solution.
Check authorization status each time the app starts, or when the app enters the foreground after the user views the Settings app.
If status is notDetermined, request authorizedWhenInUse. You can't jump straight to authorizedAlways.
If status is authorizedWhenInUse, request authorizedAlways.
The catch, as you know, is when the user goes from notDetermined to notDetermined, i.e. doesn't change their settings. We need to be able to see what the status was before they were prompted.
This is actually easy, just save the value of CLLocationManager.authorizationStatus() in a property called previousAuthStatus.
private var previousAuthStatus = CLLocationManager.authorizationStatus()
When the application enters the foreground, check the authorization state with that previous state.
Full code
Don't miss the last bit where previousAuthStatus is set
func checkStatus(_ status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
// request "When in use"
locationManager.requestWhenInUseAuthorization()
case .authorizedWhenInUse:
// already tried requesting "Always"?
guard previousAuthStatus != .authorizedWhenInUse else {
<#notify the user...#>
break
}
// otherwise, request "Always"
locationManager.requestAlwaysAuthorization()
case .authorizedAlways:
// start updating location
<#dismiss any warning you may have displayed#>
locationManager.startUpdatingLocation()
case .denied:
<#notify the user...#>
case .restricted:
<#notify the user...#>
#unknown default:
break
}
// save status for next check
previousAuthStatus = status
}
I'm facing the same problem. This is not a problem for app updates (where you did not store authStatus in UserDefaults in previous version) only. It is also a problem if a user decides to uninstall and reinstall the app (user defaults are removed), or user manually change the status from 'Always' to 'When in Use' (no way of knowing that it was changed to 'When in Use' or if it was that status all along).
Btw: the user manually changing the authorisation is actually very common, because iOS now brings up an alert every once in a while "AppName has been using your location in the background... do you want to continue allowing this" (or something like that don't remember exactly). Lots of my users choose 'No' in that alert, resulting in the 'Always' location access being changed to 'When in Use' without the app getting a notification about that.
iOS however does remember if requestAlwaysAccess was already asked before, no matter what. Even after an uninstall and reinstall of the app.
So, with lack of other options I'm now using this, which honestly, is not the most user friendly, but it does work and is user friendly 'enough' (for me at least).
In stead of requesting always authorisation, I simply bring up an alert, with one of the buttons pointing to the apps Settings page, where user can then manually change the setting. I did add a userdefault storing if the app showed this alert before. If it did, I won't show it again.
switch CLLocationManager.authorizationStatus() {
case .restricted, .denied:
// show an alert with one button opening settings (see below)
case .authorizedAlways:
// already have always permission, continue with your code
case .notDetermined:
// request whenInUse authorisation (you can request always authorisation here too, but iOS won't show 'always' as a choice the first time)
case .authorizedWhenInUse:
guard !UserDefaults.showedNoAlwaysAuthorisationAlert else {
return
}
UserDefaults.showedNoAlwaysAuthorisationAlert = true
// show the alert with "no thanks" and "settings" button
// button action:
if 'settingsButtonTapped', let settingsUrl = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(settingsUrl) {
UIApplication.shared.open(settingsUrl, completionHandler: nil)
}
}

App crashes when requesting location and user does nothing

I have a button that uses the user's location to search around them. If they have not granted/denied permission to use their location yet, it requests the permission
Relevant code:
func updateLocationStatus(){
locationEnabled = CLLocationManager.locationServicesEnabled()
appLocationEnabled = CLLocationManager.authorizationStatus()
}
#IBAction func searchNearby(sender: UIButton) {
updateLocationStatus()
if appLocationEnabled == CLAuthorizationStatus.Denied {
//Show an alert to let them know they've denied location permissions
presentViewController(alert, animated: true, completion: nil)
}
else if appLocationEnabled == CLAuthorizationStatus.NotDetermined ||
appLocationEnabled != CLAuthorizationStatus.AuthorizedWhenInUse &&
appLocationEnabled != CLAuthorizationStatus.AuthorizedAlways{
locationManager.requestWhenInUseAuthorization()
}
else{
activityIndicator.startAnimating()
locationManager.requestLocation()
}
}
It works fine when used normally. However, if you let the "Allow app to use your location [Don't Allow] [Allow]" alert sit there for about 5 seconds, the app crashes. The line jumped to in Xcode causing the error is
locationManager.requestWhenInUseAuthorization()
and the error provided is
Thread 1: EXC_BAD_ACCESS (code=2, address=0xsomeaddress)
I don't even know where to begin to fix this because the error tells me nothing and the functionality works perfectly fine, except for when you do nothing too long
Does the backtrace provide any helpful information? In the console, you can type bt at the prompt when Xcode breaks. Alternately, you can take a look at the debug navigator.
Perhaps this will solve this question. Make sure to add the appropriate key-value combo in your Info.plist.
Kind of like this:
Also you might want to set a break point to make sure that location definitely has been enabled.
To start finding the location, you should also configure the desiredAccuracy and distanceFilter properties of the location manager and then call the requestLocation() or startUpdatingLocation().

CoreLocation Authorization issues - never prompted for authorization

I was testing out CoreLocation to learn how to capture and record the location of the user. I built a simple Master-Detail app which, in the detail pane, showed the user's live location. Everything worked as expected.
Next, I moved on to making my real app which also uses CoreLocation. I built the app with a Master-Detail style and also made it so that when the user opens the detail pane, it should show their current, live, location. However, nothing happens at all.
What I determined after a LOT of debugging and research is that as soon as my locationManager is created and I call locationManager.startUpdatingLocation() the authorization to get the location is changed to denied (or false, or whatever its called). The only way I can get live tracking is build and run the app in Xcode, once it opens in the simulator, open up Location Services and change the app setting to allow location tracking to "Always". Then I can see in the console that locations are being acquired.
I don't understand why I have to keep changing the authorization to "Always" over and over. I have deleted the app on the simulator, done a "clean" from XCode to start fresh, and still it starts on the simulator without authorization.
Anyone have ideas?
UPDATE Now I see that, when I build and it runs in the simulator, I get ONE location, then the authorization is changed to not allow location services.
Here's my code (which worked on another app):
import UIKit
import CoreLocation
class DetailViewController: UIViewController, CLLocationManagerDelegate {
var locationManager : CLLocationManager!
func startTrackingLocation() {
println("tracking engaged")
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization() // <-- this was originally commented out
locationManager.startUpdatingLocation()
println("and we're tracking")
println(locationManager.location)
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
println("location acquired") // <--- Only see this when I manually allow location when app is running
self.newLongLabel?.text = "\(locations[0].coordinate.longitude)"
self.newLatLabel?.text = "\(locations[0].coordinate.latitude)"
}
override func viewDidLoad() {
super.viewDidLoad()
self.startTrackingLocation()
self.configureView()
}
// more class stuff...
If you're certain you have added the key to plist file try checking for it in the didChangeAuthorizationStatus: location manager delegate method.
Be sure you have both keys in plist as type string and with the values you would like to display to the user:
NSLocationAlwaysUsageDescription
NSLocationWhenInUseUsageDescription
- (void) locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status{
if (status == kCLAuthorizationStatusAuthorizedWhenInUse || status == kCLAuthorizationStatusAuthorizedAlways){
if ([CLLocationManager locationServicesEnabled]) {
[yourLocationManager startMonitoringSignificantLocationChanges];
//run your code here
}
}
}

Resources