If user has explicitly denied authorization for this application, or location services are disabled in Settings, it will return Denied status. How can I know the exact reason of it?
I've made this two function to check for each case
If user explicitly denied authorization for your app only you can check it like this,
+ (BOOL) isLocationDisableForMyAppOnly
{
if([CLLocationManager locationServicesEnabled] &&
[CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied)
{
return YES;
}
return NO;
}
If location services are disabled in Settings,
+ (BOOL) userLocationAvailable {
return [CLLocationManager locationServicesEnabled];
}
And I'm using it like this,
if([UserLocation userLocationAvailable]) {
//.... Get location.
}
else
{
if([UserLocation isLocationDisableForMyAppOnly]) {
//... Location not available. Denied accessing location.
}
else{
//... Location not available. Enable location services from settings.
}
}
P.S. UserLocation is a custom class to get user location.
Swift
Use:
CLLocationManager.authorizationStatus()
To get the authorization status. It's a CLAuthorizationStatus, you can switch on the different status' like this:
let status = CLLocationManager.authorizationStatus()
switch status {
case .authorizedAlways:
<#code#>
case .authorizedWhenInUse:
<#code#>
case .denied:
<#code#>
case .notDetermined:
<#code#>
case .restricted:
<#code#>
}
Swift:
I've made this two function to check for each case
If user explicitly denied authorization for your app only you can check it like this,
class func isLocationDisableForMyAppOnly() -> Bool {
if CLLocationManager.locationServicesEnabled() && CLLocationManager.authorizationStatus() == .denied {
return true
}
return false
}
If location services are disabled in Settings,
class func userLocationAvailable() -> Bool {
return CLLocationManager.locationServicesEnabled()
}
And I'm using it like this,
if UserLocation.userLocationAvailable() {
//.... Get location.
} else {
if UserLocation.isLocationDisableForMyAppOnly() {
//... Location not available. Denied accessing location.
} else {
//... Location not available. Enable location services from settings.
}
}
P.S. UserLocation is a custom class to get user location.
Swift 5.0 iOS 14.0
Since 'authorizationStatus()' was deprecated in iOS 14.0 you should use the instance directly.
authorizationStatus is now a property of CLLocationManager.
You can use the following example:
import CoreLocation
class LocationManager {
private let locationManager = CLLocationManager()
var locationStatus: CLAuthorizationStatus {
locationManager.authorizationStatus
}
}
Have also a look at this question: AuthorizationStatus for CLLocationManager is deprecated on iOS 14
Related
I don't know can I use this functionality in my UI tests on iOS, but I try it, an have problem with this.
In my UI tests I can choose Allow tracking for my app or I can decline tracking, but after all these actions, I want checkout status IDFA via ATTrackingManager.AuthorizationStatus, but this method always returns notDetermined. If I go to Settings > Privacy > Tracking, here I see that settings applied correctly (switch Allow App To Request To Track is on and switch for my app in right state (on or off)).
I don't have any idea why I recieve wrong AuthorizationStatus.
Here is my code in my XCTestCase:
import AppTrackingTransparency
enum TrackingStatus {
case authorized
case denied
case notDetermined
}
func map(_ status: ATTrackingManager.AuthorizationStatus) -> TrackingStatus {
switch ATTrackingManager.trackingAuthorizationStatus {
case .notDetermined:
return .notDetermined
case .authorized:
return .authorized
default:
return .denied
}
}
func advertisingTrackingStatusCheckout(status: TrackingStatus) {
print("IDFA status: \(ATTrackingManager.trackingAuthorizationStatus)")
var currentTrackingStatus: TrackingStatus {
return map(ATTrackingManager.trackingAuthorizationStatus)
}
guard currentTrackingStatus == status else {
XCTFail("IDFA status: \(currentTrackingStatus), expected: \(status)")
return
}
}
After settings IDFA status in my UI test, i call this method, ex. advertisingTrackingStatusCheckout(status: TrackingStatus.denied)
But it always returns notDetermined.
It behaviors have only one exception: If I manually set switch Allow App To Request To Track to off-state, calling the ATTrackingManager.trackingAuthorizationStatus will returns denied.
Delete the App, And call your function in sceneDidBecomeActive with delay. So once your app become active then it will shown. Am facing the same issue now its resolved. Like this
func sceneDidBecomeActive(_ scene: UIScene) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.requestPermission()
}
}
func requestPermission() {
if #available(iOS 14, *) {
ATTrackingManager.requestTrackingAuthorization { status in
switch status {
case .authorized:
// Tracking authorization dialog was shown
// and we are authorized
print("Authorized Tracking Permission")
// Now that we are authorized we can get the IDFA
case .denied:
// Tracking authorization dialog was
// shown and permission is denied
print("Denied Tracking Permission")
case .notDetermined:
// Tracking authorization dialog has not been shown
print("Not Determined Tracking Permission")
case .restricted:
print("Restricted Tracking Permission")
#unknown default:
print("Unknown Tracking Permission")
}
}
} else {
// Fallback on earlier versions
}
}
We are working on an app which needs location subsequently on terminated state. I am trying to achieve this by enabling Background Modes services i.e location update and background fetch, but app doesn't relaunch silently after a significant change in location. I have Enabled properties like startMonitoringSignificantLocationChanges and allowsBackgroundLocationUpdates with my location manager on didFinishLaunchingWithOptions and on didUpdateLocations create CLRegion and enable locationManager startMonitoring(for: region) but all this doesn't works.
This is what I have written in my didFinishLaunchingWithOptions
if application.applicationState == .active {
LocationManager.shared.startMonitoringLocation()
}
// When there is a significant changes of the location,
// The key UIApplicationLaunchOptionsLocationKey will be returned from didFinishLaunchingWithOptions
// When the app is receiving the key, it must reinitiate the locationManager and get
// the latest location updates
// This UIApplicationLaunchOptionsLocationKey key enables the location update even when
// the app has been killed/terminated (Not in th background) by iOS or the user.
if launchOptions?[UIApplication.LaunchOptionsKey.location] != nil {
//You have a location when app is in killed/ not running state
LocationManager.shared.afterResume = true
LocationManager.shared.startMonitoringLocation()
LocationManager.shared.addApplicationStatus(toPList: "APP relaunch silently")
}
Location Manager
func startMonitoringLocation() {
if (anotherLocationManager != nil) {
anotherLocationManager!.stopMonitoringSignificantLocationChanges()
}
anotherLocationManager = CLLocationManager()
anotherLocationManager!.allowsBackgroundLocationUpdates = true
anotherLocationManager!.pausesLocationUpdatesAutomatically = false
anotherLocationManager?.delegate = self
anotherLocationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation
anotherLocationManager?.activityType = .otherNavigation
if CLLocationManager.locationServicesEnabled() {
switch CLLocationManager.authorizationStatus() {
case .notDetermined, .restricted, .denied:
print("No access")
case .authorizedAlways, .authorizedWhenInUse:
print("Access")
}
} else {
anotherLocationManager?.requestAlwaysAuthorization()
print("Location services are not enabled")
}
anotherLocationManager?.startMonitoringSignificantLocationChanges()
}
The problem is control doesn't come into this code snippet.
if launchOptions?[UIApplication.LaunchOptionsKey.location] != nil {
//You have a location when app is in killed/ not running state
}
It is my general ViewController class , I research the this question iOS app doesn't ask for location permission , I already have NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription
#IBOutlet weak var ownMapView: MKMapView!
let locationManager : CLLocationManager = CLLocationManager();
override func viewDidLoad() {
super.viewDidLoad();
self.locationManager.delegate = self;
self.locationManager.requestWhenInUseAuthorization();
self.locationManager.startUpdatingLocation();
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
guard status == .AuthorizedWhenInUse else
{
print("Location not using");
return;
}
print("Location using.");
ownMapView.showsUserLocation = true;
}
Even I added NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription this is not working for me . It prints only "Location not using"
How do I request correctly ?
To understand this correctly check the CLLAuthorizationStatus doc:
https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/#//apple_ref/c/tdef/CLAuthorizationStatus
CLAuthorizationStatus has several possible values:
NotDetermined - User has not yet made a decision to allow/deny to use location
Restricted - App is restricted to use location
Denied - User has denied to use location.
AuthorizedAlways - This app is authorized to start location services at any time.
AuthorizedWhenInUse - App is authorized to start most location services while running in the foreground
You are checking only "AuthorizedWhenInUse" status but you launch first its status is NotDetermined.
You can further check the status like below:
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .NotDetermined
{
print ("Loction use not determined")
return
}
if status == .Denied
{
print ("Location determined")
return
}
guard status == .AuthorizedWhenInUse else
{
print("Location not using");
return;
}
print("Location using.");
ownMapView.showsUserLocation = true;
}
Have us added NSLocationAlwaysUsageDescription & NSLocationWhenInUseUsageDescription in plist? . Add these and it will work .
I'm trying to set up an on boarding where I'm asking the user for a few permissions including: Location, Notifications and Camera. I have 3 different View Controllers set up, each asking for one of the permissions and explaining why. On each of the view controllers I have a button at the bottom that says "Grant Permission".
When the user clicks the button I want the permission dialogue to pop up, and once the user clicks allow I want to transition to the next view controller.
Here is what I have right now:
class OnboardingStep2:UIViewController{
override func viewDidLoad() {
self.view.backgroundColor = StyleKit.orangeWhite()
}
#IBAction func getPermission(sender: AnyObject) {
dispatch_sync(dispatch_get_main_queue()) {
let locManager = CLLocationManager()
locManager.requestAlwaysAuthorization()
}
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.Authorized) {
self.performSegueWithIdentifier("goToStep3", sender: self)
}
}
}
I've tried using dispatch to queue up the tasks, but when using async the permission dialogue pops up and then immediately it closes because the authorization check is run (I'm assuming). Using dispatch_sync, the dialogue is never shown.
What is the best way to do this, I want the permission dialogue to pop up first and once the user clicks allow i want to segue.
Conform to the CLLocationManagerDelegate
Then call this:
Swift 3.0
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
manager.requestLocation()
case .authorizedAlways, .authorizedWhenInUse:
// Do your thing here
default:
// Permission denied, do something else
}
}
Swift 2.2
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .NotDetermined:
manager.requestLocation()
case .AuthorizedAlways, .AuthorizedWhenInUse:
// Do your thing here
default:
// Permission denied, do something else
}
}
Swift 5
Implement CLLocationManagerDelegate
and this function:
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
// User has not yet made a choice
case .denied:
// User has explicitly denied authorization
case .restricted:
// This application is not authorized to use location services.
case .authorized, .authorizedAlways, .authorizedWhenInUse:
// User has granted authorization
default:
// Other
}
}
I'm trying to build a permission determiner class, which basically determine the permission.
So far I've done below code, however I keep getting error in the case statements case LocationUsage.WhenInUse: and case .Always:.
It says that
enum case is not a member fo type PrivateResoure.LocationUsage?
What am I doing wrong in this small struct?
public struct PrivateResource {
public enum LocationUsage {
case WhenInUse
case Always
}
var usage: LocationUsage?
public var isNotDeterminedAuthorization: Bool {
return CLLocationManager.authorizationStatus() == .NotDetermined
}
public var isAuthorized: Bool {
switch usage {
case LocationUsage.WhenInUse:
return CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse
case .Always:
return CLLocationManager.authorizationStatus() == .AuthorizedAlways
}
}
}
I see two problems:
usage is an Optional, so you have to unwrap it.
.AuthorizedWhenInUse and .AuthorizedAlways are not part of the CLAuthorizationStatus choices.
The choices are:
Authorized, Denied, NotDetermined, Restricted
A quick fix:
public var isAuthorized: Bool {
switch usage! {
case LocationUsage.WhenInUse:
return CLLocationManager.authorizationStatus() == .Authorized
case .Always:
return CLLocationManager.authorizationStatus() == .Authorized
}
}
Two notes: it solves the issue but my example solution changes your logic - you may have to adapt. Also, I've force unwrapped the Optional for this example: I'll let you choose the safest way instead that is adapted for your code.