I'm working in a ios recursive function to send the message 'My App need request your location' each time that the user turn off his GPS then, the message should appears again requesting his location.
I'm just using:
public bool LocationServicesEnabled {
get {return CLLocationManager.LocationServicesEnabled;}
}
public bool IsAuthorized {
get {return CLAuthorizationStatus.Authorized == _authstatusLast;}
}
With those functions, the first one return me is the Service is Enable and the second one if the user autorizate it.
Just now, I can do something similar to:
if(_locationServiceEnalbed && IsAuthorized){
Manager = new LocationManager();
Manager.StartLocationUpdates();
}
But is not requesting me my location again.
How I can display the message again requesting the user location?
According to Apple, if they deny access to your app, you can request it again by trying to start the location services
If it returns NO and you attempt to start location services anyway,
the system prompts the user to confirm whether location services
should be re-enabled. Because the user probably disabled location
services on purpose, the prompt is likely to be unwelcome.
However, a more friendly approach might be to display a message that this feature of your app is disabled until they grant permissions to use GPS, and to direct them to the Settings menu where it can be re-enabled
Related
I am new to Xamarin iOS and mobile dev in general.
I have a application which requires location services, on my view controller I have a button which takes the user to location settings for the app, however, if the main device location is off the user will not be able to do anything with the app level location setting.
I am using this code on my button click event to take the user to the settings page.
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
NSString settingsString = UIApplication.OpenSettingsUrlString;
NSUrl url = new NSUrl(settingsString);
UIApplication.SharedApplication.OpenUrl(url);
}
I would like to know if there is a way to check if device level Location services are off and take the user to that settings page instead of app-level location settings and vice versa.
Also how to take users to Location settings screen if device level location services is disabled. I tried a few combinations but I am unsure what the NSUrl will be.
To check the Device level location permission:
bool deviceLevel = CLLocationManager.LocationServicesEnabled;
Document here: determining_the_availability_of_location_services
To check the app level location permission:
public void CheckAuthorization(CLLocationManager manager, CLAuthorizationStatus status)
{
switch (status)
{
case CLAuthorizationStatus.Authorized | CLAuthorizationStatus.AuthorizedAlways | CLAuthorizationStatus.AuthorizedWhenInUse:
Console.WriteLine("Access");
break;
case CLAuthorizationStatus.Denied | CLAuthorizationStatus.NotDetermined | CLAuthorizationStatus.Restricted:
Console.WriteLine("No Access");
break;
default:
Console.WriteLine("No Access");
break;
}
}
Document here: clauthorizationstatus
Update:
Have a look at answers in there two threads: how-to-programmatically-open-settings-privacy-location-services-in-ios-11 and how-to-open-location-services-screen-from-setting-screen
There says
Avoid use of "prefs:root" or "App-Prefs:root" in you app, otherwise
App will be rejected from App Store. Just open Setting page.
You can not open the device location permission directly, it is not allow through App Store rules.
Just use UIApplication.OpenSettingsUrlString; to open the setting page.
Welcome to mobile and Xamarin! Yes there are several Nuget Packages you can add that will help you do this. One that's gaining popularity is Xamarin Essentials.
As they show in the documentation, just try to get the location, it will handle permissions by itself, and if you face PermissionException, then you can open the settings as you are! Happy Coding
You can check if the user has disabled the Location services at the settings level then check the app level:
if(!CLLocationManager.LocationServicesEnabled)
{
Console.WriteLine("Location Services are off globally go to settings");
// This may get your app rejected using the strings below
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
UIApplication.SharedApplication.OpenUrl(new NSUrl("App-Prefs:root=General"));
}
else
{
UIApplication.SharedApplication.OpenUrl(new NSUrl("prefs:root=General"));
}
}
else if (CLLocationManager.Status == CLAuthorizationStatus.Denied ||
CLLocationManager.Status == CLAuthorizationStatus.NotDetermined ||
CLLocationManager.Status == CLAuthorizationStatus.Restricted)
{
Console.WriteLine("Location Services are off just for your app, got to app settings");
UIApplication.SharedApplication.OpenUrl(new NSUrl(UIApplication.OpenSettingsUrlString));
}
In terms of opening to the system settings or the app settings, UIApplication.OpenSettingsUrlString will go to the app settings as per the docs:
UIApplicationOpenSettingsURLString
Used to create a URL that you can pass to the openURL: method. When you open the URL built from this string, the system launches the Settings app and displays the app’s custom settings, if it has any.
You can use the string:
prefs:root=General
or for iOS 10 and above
App-Prefs:root=General
But Apple may reject your app, tbh I think it's not worth trying to go to the settings just for this reason but up to you.
When my app is launched it requests to use location services.
If a user selects 'Don't Allow' I prompt again letting them know that Location Services are required for the best experience and they can enable in the settings app.
If a user does not allow and still creates an account, the main screen will not fully function without the location feature part.
From this point, if I manually enable in the Settings app I'm still not getting the main page to pick up the current location.
How do I detect that location services have been enabled from the Settings App?
Is there a method I need to enforce again from the AppDelegate?
You can tell if location has been enabled for your app using CLLocationManager.AuthorizationStatus, which returns a member of the CLAuthorizationStatus enum. If location is disabled completely, your app won't be authorized, so you know everything you need to know.
let authorization = CLLocationManager.authorizationStatus()
if authorization == .AuthorizedWhenInUse || authorization == .Authorized {
...
}
If you request authorization using CLLocationManager and the user denies it, you can't cause the window to come up again.
From a UX point of view, be careful about nags as well. Communicate clearly that your app benefits from using location, but try to avoid browbeating the user about it.
See also how to determine when settings change on ios to have your app retry location access right after a user (hopefully) enabled it.
Instead of system default below popup for core location permissions, how can I use my default pop up for my app?
And when does this popup come? Is it coming on particular delegate Method?
Is there any way to disable that?
You can not replace that UIAlertView but cutomize the text to hopefully explain better why your app needs access to the user's location.
See https://stackoverflow.com/questions/12562152/replacement-for-purpose-property-of-cllocationmanager
This popup comes when you access this locationServicesEnabled property of CLLocationManager.
[CLLocationManager locationServicesEnabled];
You can handle this in your app. But anyway, you cann't handle it first time according to apple's doc(see locationServicesEnabled topic.
locationServicesEnabled
Returns a Boolean value indicating whether location services are enabled on the device.
+ (BOOL)locationServicesEnabled
Return Value
YES if location services are enabled; NO if they are not.
Discussion
The user can enable or disable location services from the Settings application by toggling the Location Services switch in General.
You should check the return value of this method before starting
location updates to determine whether the user has location services
enabled for the current device. Location services prompts users the
first time they attempt to use location-related information in an app
but does not prompt for subsequent attempts. If the user denies the
use of location services and you attempt to start location updates
anyway, the location manager reports an error to its delegate.
After disallow, try to attempt,
if ([CLLocationManager locationServicesEnabled)
{
//Do your work
}
else
{
if ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorized)
{ //Show Alert view... }
}
When the user launches the app for the first time and attempts to login, they are prompted with the iOS dialog - "Turn On Location Services".
I need to capture when the user clicks "cancel". Is there a Notification sent? If so, what is its name? I've been unable to locate it.
The CLAUthorizationStatus is kCLAuthorizationDenied when Location Services are Disabled OR the user clicked "Don't allow". When the user clicks "Cancel", it does not fire the authorizationChange event. When user clicks "Cancel", the app just hangs.
Short answer: You can't catch that notification. You can infer about the user choice and act consequently by using CLLocationManager methods (the longer answer below).
Longer answer:
Firstly, welcome on Stack Overflow. Before kindly posing your question, and trying to be collaborative with people that are here to help, it's a good idea to search if somebody else previously posed the same question.
A brief search gave (just to mention some of them):
How to handle “Cancel” button on Alert pop up for Location services
How to get location services to reprompt the user for location permission if they accidentally refused it?
locationManager:didFailWithError: not called if user Location Services are off
How to prompt user to turn on Location Services…again
How can I prompt the user to turn on location services after user has denied their use
How to ask permission from user for second time to allow to access the current location?
Now, let's try to summarize them all, starting from iOS docs:
If your app relies on location services to function properly, you should include the UIRequiredDeviceCapabilities key in the app’s Info.plist file. You use this key to specify the location services that must be present in order for your app to run. The App Store uses the information in this key from preventing users from downloading apps to devices that do not contain the listed features.
Important: If your app uses location services but is able to operate successfully without them, do not include the corresponding strings in the UIRequiredDeviceCapabilities key.
So, if your app really needs to access the user's position you should add location-services and eventually gps to UIRequiredDeviceCapabilities.
Then, somewhere in your code - when needed, you have to check if the location services are enabled.
[CLLocationManager locationServicesEnabled]
they may be disallowed for three reasons:
The user can disable location services in the Settings app.
The user can deny location services for a specific app.
The device might be in Airplane mode and unable to power up the necessary hardware.
You are interested in the second case: the user refused to allow your app to use the location services.
Again, from the docs:
Important: In addition to hardware not being available, the user has the option of denying an application’s access to location service data. During its initial uses by an application, the Core Location framework prompts the user to confirm that using the location service is acceptable. If the user denies the request, the CLLocationManager object reports an appropriate error to its delegate during future requests. You can also check the application’s explicit authorization status using the authorizationStatus method.
[CLLocationManager authorizationStatus]
That may return:
kCLAuthorizationStatusNotDetermined if the user has not yet made a choice regarding whether this application can use location services.
kCLAuthorizationStatusRestricted this application is not authorized to use location services. The user cannot change this application’s status, possibly due to active restrictions such as parental controls being in place.
kCLAuthorizationStatusDenied The user explicitly denied the use of location services for this application or location services are currently disabled in Settings.
kCLAuthorizationStatusAuthorized This application is authorized to use location services.
If[CLLocationManager locationServicesEnabled] returns NO and you attempt to start location services anyway (i.e. calling [locationManager startUpdatingLocation]), the system prompts the user to confirm whether location services should be re-enabled. Given that location services are very likely to be disabled on purpose, the user might not welcome this prompt.
I suppose you know, and did all the previous steps (I'm only sure you checked the authorizationStatus). You refused to show us the significant code of your app so I can only suppose the overall logic behind. Now you said your app hangs. This should be because you didn't catch the error properly? Catching the error is the way to re-prompt the user, if you wish.
After calling [locationManager startUpdatingLocation], if not authorized, your delegate should define a locationManager:didFailWithError: in order to catch the kCLErrorDenied.
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
You may show, at this point, a UIAlert to insist asking the user to give you access to its position, or trigger a change in the UI or whatever you like.
Final notes
I hope you understand why I was asking for the code: the reason was to offer you an alternative solution instead of reply "You can't catch the 'Cancel' notification".
If this answer does not satisfy your question please elaborate why you need to catch the pushing of the "Cancel"/"Do not allow" button, so we can provide alternatives.
Clearly my advice is to not annoy people to death by continuously ask them for enabling location services if they don't want.
Post scriptum: Maybe that the answer looks pedantic and obvious in certain parts if not all to you, but we are here to provide answers also for future readers.
If we try to access the user's location, iOS will tell the user that our application wants to use their location.
If I do this
[locationManager startUpdatingLocation];
An alert will show.
However, that only happens once.
It looks like some variable or default must have been set up once that display pops out.
How do I reset those default so that next time my app wants to use location users will be asked again?
Google map can displays that again and again.
It's Apple that asks them for permission, not you
Translation: You don't have any control over that part of the process. The little popup:
is only shown by Apple when you first ask for it - so the user always feels in control. After they accept for the first time, Apple assumes they are OK with your app getting their location information from their device, and won't show it again*.
*Unless they specifically go into Settings and disable Location Services for you app.
It's only showed on the first time and there's nothing you can do to change that. What you can do is ask your users to allow it on settings.
You can check if your app has permissions by checking:
[CLLocationManager locationServicesEnabled]
From the docs:
The user can enable or disable location services from the Settings application by toggling the Location Services switch in General.
You should check the return value of this method before starting location updates to determine whether the user has location services enabled for the current device. If this method returns NO and you start location updates anyway, the Core Location framework prompts the user to confirm whether location services should be reenabled.