NOTE: My questions is not a duplicate of questions asking HOW to detect if the user has denied access.
I saw a lot of code on HOW to detect if the user has given access to the camera, but I'd need to know WHEN the user denies access.
My scenario is the next:
User opens view A, A asks for access to the camera, pop up appears, user denies access, user is redirected to B.
Is this possible?
Actually, there's a way of doing it:
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if(granted){
NSLog(#"Granted access");
} else {
NSLog(#"Not granted access");
}
}];
As of now you can use applicationWillResignActive: and applicationDidBecomeActive:, verify whether the AVAuthorizationStatus's been changed and do whatever is needed. This isn't a future-proof solution, as the new iOS versions might not bring the app to background when asking for camera permissions, however any modifications to UIAlertView are even worse for that matter.
Related
The requestRecordPermission function memorize user's first time choice & it doesn't show the granting record permission alert if user has previously denied recording permission.
How can I always pop up the granting recording permission alert if previously user denied recording permission?
[[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
if (!granted) {
// Microphone permission is not granted previously,
// How to pop up granting alert/dialog again?
// (My app supports iOS 7 and above)
}
}];
I know how to detect whether the permission is granted or not, my question is about after recording(microphone) permission has been denied once, how can I present the granting permission alert again to user?
(My app needs to support iOS7 and above)
Actually it's not possible to show permission alert again!
check out this SO post:
Request permissions again after user denies...
It says-
The OS will only ever prompt the user once. If they deny permission, that's it.
Following the Apple guidelines, you should simply show a message to the user explaining why he can't use the record feature.
A simple "How to enable my record permission" will do the job :)
I have an app that uses the Address Book. When running in iOS 6 it runs this code when the user does something that requires Address Book access.
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)
{
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error)
{
if (granted)
{
showContactChooser();
}
});
CFRelease(addressBookRef);
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized)
{
showContactChooser();
}
else
{
showAccessDeniedAlert();
}
This works perfectly: I am able to read the contacts information and when the user denied access, the app reacts accordingly.
However, if the user:
Allows Contacts access in the app,
Quits the app,
Goes to Settings->Privacy->Contacts and disables Contacts access for the app,
Runs the app,
While the app is running in background goes to settings and enables Contact access for the app,
the app immediately crashes inside main() with no exception information or a meaningful stack trace. I tried turning on the "all exceptions" and [NSException raise] breakpoint, but that didn't give me any more information.
The crash can be reproduced even if the app doesn't run the above code during the launch.
What's happening here? Is there a callback that I should be subscribing to?
I've seen this in my own app. And I've seen others report this as well. I'm pretty sure this is deliberate behavior. The OS kills any background apps that react to changes in privacy permissions. Apple appears to have taken a sledgehammer approach to this. It's not a crash (though it may appear so when running in the debugger). Apps get terminated for various other reasons. Add this to the list of reasons. This gives us more reason to do a good job restoring app state upon a full restart of our apps.
Note that this behavior applies to all of the various privacy settings such as contacts, photos, microphone, calendar, and camera.
Usually, when an application comes back from being suspended, it should call application:didEnterForeground from your AppDelegate. In my opinion, that would be a good place for you to readjust your address book permissions.
I have an app that uses the Address Book. When running in iOS 6 it runs this code when the user does something that requires Address Book access.
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)
{
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error)
{
if (granted)
{
showContactChooser();
}
});
CFRelease(addressBookRef);
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized)
{
showContactChooser();
}
else
{
showAccessDeniedAlert();
}
This works perfectly: I am able to read the contacts information and when the user denied access, the app reacts accordingly.
However, if the user:
Allows Contacts access in the app,
Quits the app,
Goes to Settings->Privacy->Contacts and disables Contacts access for the app,
Runs the app,
While the app is running in background goes to settings and enables Contact access for the app,
the app immediately crashes inside main() with no exception information or a meaningful stack trace. I tried turning on the "all exceptions" and [NSException raise] breakpoint, but that didn't give me any more information.
The crash can be reproduced even if the app doesn't run the above code during the launch.
What's happening here? Is there a callback that I should be subscribing to?
I've seen this in my own app. And I've seen others report this as well. I'm pretty sure this is deliberate behavior. The OS kills any background apps that react to changes in privacy permissions. Apple appears to have taken a sledgehammer approach to this. It's not a crash (though it may appear so when running in the debugger). Apps get terminated for various other reasons. Add this to the list of reasons. This gives us more reason to do a good job restoring app state upon a full restart of our apps.
Note that this behavior applies to all of the various privacy settings such as contacts, photos, microphone, calendar, and camera.
Usually, when an application comes back from being suspended, it should call application:didEnterForeground from your AppDelegate. In my opinion, that would be a good place for you to readjust your address book permissions.
Is it possible to differentiate between the cases where
an iOS user has explicitly denied user notification permissions, and
an iOS user has never been prompted for permission?
My situation: In the past, I've prompted for user notification permission, but never kept track of requests myself. Later, I stopped attempting to register any notification settings. Now, I'd like to re-introduce user notifications.
After a significant event in the App, my plan is to display some sort of UI that explains the benefit of opting in to user notifications. However, if the user has already declined, I'd prefer to show a separate UI that can take them into Settings.app.
Currently, I'm using -[UIApplication currentUserNotificationSettings] to grab the current settings, but it appears that this returns UIUserNotificationTypeNone for both of the above described cases.
Personally I haven't found a way to determine this via a quick query of the iOS SDK.
However I have been able to track this myself recording when -[UIApplication application:didRegisterUserNotificationSettings:] is called.
When iOS calls this method, you can be sure the user has been prompted for user notification permissions and has (importantly) either accepted or denied it.
Storing when this occurs you can later check this value to determine if the prompt has been shown before or not.
Example Code:
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"ABHasPromptedForUserNotification"];
//... your other notification registration handling...
}
- (BOOL)hasPromptedForUserNotification {
return [[NSUserDefaults standardUserDefaults] boolForKey:#"ABHasPromptedForUserNotification"];
}
FYI: I've found it preferable to set "ABHasPromptedForUserNotification" as true in the in -[UIApplication application:didRegisterUserNotificationSettings:] rather than when I call -[UIApplication registerForRemoteNotifications] as in some situations the user can be shown the prompt multiple times. This can happen if the user backgrounds the app, or takes a call. In these cases the prompt will be hidden by iOS, and shown again if next time you call -[UIApplication registerForRemoteNotifications]. Setting this setting in the delegate avoids thinking the user has been prompted before and won't be prompted again in these edge cases.
No.
And I believe this is done intentionally. Because usual scenario is to register for remote notifications on every app launch. That means that user should not see permissions dialog each time he opens the app. iOS do this automatically. But if you will show extra screen before requesting permissions Apple can't allow you to know if user denied permissions in the past so you can show screen describing how user can enable his permissions through settings each time you want. This will undo all Apple did to stop irritating users.
In your case you must follow same strategy. Show only one type of explanatory screen on both scenarios and save user choice in NSUserDefaults to know if you must not show it again. Users who denied permissions previosly will not see permissions dialog. Though you'll have one benefit for new users (which is obviously you are trying to achieve): you may keep showing explanatory screen many times if user canceled it.
If you support iOS 10 and above the UNUserNotifications framework allows more granularity.
let current = UNUserNotificationCenter.current()
current.getNotificationSettings(completionHandler: { (settings) in
if settings.authorizationStatus == .notDetermined {
// Not requested
}
if settings.authorizationStatus == .denied {
// User said Don't allow
}
})
In case someone need to check the old UIUserNotification API for access permission. code below is tried and tested.
- (BOOL)isUserNotificationAllowed {
UIUserNotificationType types = [[UIApplication sharedApplication] currentUserNotificationSettings].types;
if(types & UIUserNotificationTypeBadge || types & UIUserNotificationTypeSound || types & UIUserNotificationTypeAlert){
return YES;
}
else {
return NO;
}
}
Update for iOS 10.0+.
Apple provide this for checking user's push notification permission status:
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
switch (settings.authorizationStatus) {
case UNAuthorizationStatusNotDetermined:
DDLogDebug(#"Not Determined");
break;
case UNAuthorizationStatusDenied:
DDLogDebug(#"Denied");
break;
case UNAuthorizationStatusAuthorized:
DDLogDebug(#"Authorized");
break;
case UNAuthorizationStatusProvisional:
DDLogDebug(#"Provisional");
break;
}
}];
Check Apple Doc for more information.
Is there a way to interact with the alerts show by iOS.For eg: If my app has registered itself for APNS,on first launch, iOS shows an UIAlertView(I am assuming,it is one),giving the user two choices.Is there a way to find out which button the user selected?
I have two alerts that are shown ,during my app launch,one for APNS and the other for Location Services.Is there a way to identify which alert is for what?
In case you can't access those alerts directly I suggest you to look at this problem from another point of view.
For CoreLocation for example you can look at its [CLLocationManager authorizationStatus].
kCLAuthorizationStatusNotDetermined = 0, // User wasn't proposed to use location services
kCLAuthorizationStatusRestricted, // Parental control or something like that
kCLAuthorizationStatusDenied, // User didn't allow this application to use services
kCLAuthorizationStatusAuthorized // User allowed to use his location.
As for APNS there are [[UIApplication sharedApplication] enabledRemoteNotificationTypes]
Source:
https://developer.apple.com/library/ios/documentation/uikit/reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/c_ref/UIRemoteNotificationType
https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/CLLocationManager/CLLocationManager.html#//apple_ref/doc/c_ref/CLAuthorizationStatus
No. There is no way to get callbacks on the AlertViews created by the OS. Like CoolMonster indicated in his comment, you can find out what the user chose for that particular AlertView and do something based off that.
I don't know about push notifications but here's one (ugly) way of detecting the user's choice when asking for permissions to get user's location:
// After asking for permission, the alert is shown to the user. Since he can't do anything
// at this point but select one of the two options we simply wait...
while(([CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined))
{
sleep(1);
}
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized)
{
NSLog(#"User allowed access to gps");
}
else
{
NSLog(#"User denied access to gps");
}
You can get notification types like this:
UIRemoteNotificationType notificationTypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
And may create some hack ways to get user's interaction to Notifications question
Though there may not be a clear-cut way to know what button the user clicked on, you can certainly find out if the user authorized or not the use of push notifications by implementing these UIApplication delegate methods:
application:didRegisterForRemoteNotificationsWithDeviceToken: (User accepted)
application:didFailToRegisterForRemoteNotificationsWithError: (User denied or is not able to accept)