I am developing an app that needs to access both the user's location and calendar, a pop up should appear but for some reason it is not showing and I keep getting denied, here is the code for the calendar access :
- (void)askAuthorization
{
EKAuthorizationStatus authorizationStatus = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
BOOL needsToRequestAccessToEventStore = (authorizationStatus == EKAuthorizationStatusNotDetermined);
if (needsToRequestAccessToEventStore) {
[self.store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
if (granted) {
// Access granted
NSLog(#"User granted access");
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate userDidAuthorizeCalendarReadingsWithError:error];
});
} else {
// Denied
NSLog(#"User did not grant access. To grant access, go to Settings > Privacy > Calendars > VirtualAssitant");
[self.delegate userDidNotAuthorizeCalendarReadingsWithError:error];
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate userDidAuthorizeCalendarReadingsWithError:error];
});
}
}];
}
}
I am not getting a pop up and AuthorizationStatusForEntityType:EKEntityTypeEvent value is "denied"!
As for the location I went to settings-> privacy-> location, found it set to never for my application and I had to manually change it to always.
does anyone know why or has been through the same problem and can help me.
I tried cleaning the build folder and reseting Location and Privacy settings but when I rebuild my application the same thing happen I don't get a pop up asking for permission and the values are set to denied!
The permission pop up for any system access like camera, photo album, location, contacts, etc... will only appear once (for each access type).
Once it has been presented and then accepted or denied then it will never appear again.
Because it has come back with the permission "denied" this means it has already appeared and you tapped on "do not allow".
The only way to change the permission in the app after that is to go into the app settings and change it manually.
You can force the permission pop-up to appear again by deleting the app from your device and then installing it again.
Related
Summary:
Is it possible for iOS/App Store to reset an app's Location Service permission on app upgrade?
Detailed:
In our recent app upgrade, we believe quite a few of users that had previously granted our app "Always" Location Services permission as a result of enabling certain features were prompted with a While in Use Location Services prompt on first launch of the new version of our app. Since there is no context around this permission prompt, many of our users likely selected Don't Allow at this time.
AFAIK, it is not possible for our app to reset the user's permission settings via code. Is anyone aware of a certain version of iOS and/or device combination that would lead to this behaviour upon app upgrade?
The only time when our app would prompt users for Location Services permissions is if the authorizationStatus is set to kCLAuthorizationStatusNotDetermined. Otherwise we would start to request for location updates.
Here's a snippet of our app's launch code:
CLLocationManager *lmFollowMe = [[CLLocationManager alloc] init];
[lmFollowMe setDesiredAccuracy:kCLLocationAccuracyKilometer];
[lmFollowMe setDistanceFilter:1000];
[lmFollowMe setDelegate:self];
if ([lmFollowMe respondsToSelector:#selector(setAllowsBackgroundLocationUpdates:)]) {
[lmFollowMe setAllowsBackgroundLocationUpdates:NO];
}
[self setFollowMeLocationManager:lmFollowMe];
...
if ([CLLocationManager locationServicesEnabled] == YES) {
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
if (status == kCLAuthorizationStatusDenied) {
...
} else if (status == kCLAuthorizationStatusRestricted) {
...
} else if (status == kCLAuthorizationStatusNotDetermined) {
if ([self followMeStarted] == NO) {
[self setFollowMeStarted:YES];
[[self followMeLocationManager] requestWhenInUseAuthorization];
}
} else {
[[self followMeLocationManager] startUpdatingLocation];
}
...
Thanks!
You can not change location permission through code, but if user denied location permission then you can show alert for ask location permission with 'Setting' & 'Cancel' buttons.
If user click on 'Setting' then you can redirect user to application's setting page to update location permission by following code.
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
Thanks.
I have been working on integrating Touch ID support into an app I am working on. It is however acting very inconsistent. One of the common issues I am seeing is on a fresh app launch it works as expected, but then on backgrounding the app, and bringing it to the foreground I am getting an error back from
evaluatePolicy:localizedReason:reply:
It does not even make a lot of sense (I never see the touchid alert)
Error Domain=com.apple.LocalAuthentication Code=-1004 "User interaction is required." UserInfo=0x171470a00 {NSLocalizedDescription=User interaction is required.}
I have tried presenting the touchid alert when the app is already running, when its just foregrounded, does not seem to matter. Its broken on every time after the initial app launch.
Anyone else running into this?
For reference, here is the code I am using:
if (_useTouchId && [LAContext class]) {
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
_didPresentTouchId = YES;
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:#"Use your Touch ID to open *****" reply:^(BOOL success, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^ {
if (success) {
_isClosing = YES;
[self hide];
if (_successBlock) {
_successBlock();
}
}
else if (error && error.code != -2 && error.code != -3 && error.code != -1004) {
[[[UIAlertView alloc] initWithTitle:#"Error" message:#"Authentication failed, please enter your Pin" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles:nil] show];
}
else {
if (error) {
DDLogError(#"TouchID error: %#", error.description);
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, .6 * NSEC_PER_SEC), dispatch_get_main_queue(), ^ {
[self keyboardButtonTouched];
});
}
});
}];
}
}
Usually PIN view controllers are pushed before entering background in:
- (void)applicationDidEnterBackground:(UIApplication *)application
So app's inner information won't appear when paging through app preview images (home button double tap). I guess you are doing something similar.
The problem is that LocalAuthentication's new API requires the calling viewController to be visible.
This is why you shouldn't call your "showTouchID" function before resigning to background. Instead call "showTouchID" function when entering foreground:
- (void)applicationWillEnterForeground:(UIApplication *)application
And it should work.
Don't forget to call it also when app is first launched (in which case ..willEnterForeground will not get called).
#hetzi answer really helped me, but I have more to add on this.
Basically this error happens when your app is woken up from background and somewhere on your code you are asking for Touch ID (my case is the local authentication type, I haven't tested with the keychain type). There's no way the user can interact with Touch ID prompted while the app is running on background, hence the error message.
User interaction is required.
The reasons my app was coming from background were: Push Notifications or Apple Watch.
My fix is doing something like this on the viewDidLoad method of my initial VC:
if ([UIApplication sharedApplication].applicationState != UIApplicationStateBackground) {
[self promptForTouchID];
}
I've used != because, when your app first launches it is in the UIApplicationStateInactive state. And that state doesn't generate a Touch ID error because the prompt will appear.
I also call [self promptForTouchID] on a notification of UIApplicationWillEnterForegroundNotification, but since you know that the app will enter foreground, there's no need to check here.
In my Application when i log in, after that Home screen will load at that time in my ViewDidLoad method of Home screen check that location service is ON or not . If it is not turn on then it will appear Error Screen.
Above functionality work fine but after that user immediately go to the setting of their cell , turn ON location service and again tap on application that is running in background will load Home screen and hide Error Screen.
if([CLLocationManager locationServicesEnabled]==YES)
{
NSLog(#"Location Services Enabled");
if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"App Permission Denied"
message:#"To re-enable, please go to Settings and turn on Location Service for this app."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
[LocationGuideview setHidden:NO];
}
}
so please tell me how its possible ?????????
What you can try here is add the above code in the application delegate.m file in the applicationDidEnterForeGround and maintain a flag in the plist or user default which you can access across your project
Your code would be something like this then
- (void)applicationWillEnterForeground:(UIApplication *)application
{
//check for location service
if([CLLocationManager locationServicesEnabled]==YES)
{
NSLog(#"Location Services Enabled");
// store a value in plist or user default for further reading set it to YES or whatever you like
}else{
// Location services are not enabled set NO as flag value
}
}
Then inside your view controller in the view did load or view will appear you can read the stored value and do your stuff.
If you are using user defaults please use the synchronize method.
I want to detect location services are enabled or not and based on that I want to enable or disable the button in my app. For that I have written
if([CLLocationManager locationServicesEnabled])
// Enable button
else
// Disbale button
But, I found a strange behaviour with this method. From Settings,
1) If I turns location services OFF, for all apps than above method returns NO.(As expected)
2) If I turns locationServices ON, But OFF for my particualr app, than it returns YES.
Is this the correct behaviour. If yes, than is there any other method to find whether location services are enabled or disbaled at app level. Any thoughts.
As you are using locationServicesEnabled method to find whether the location services are enabled for your app or not, I would like to clear that locationServicesEnabled detect whether the location services are enabled for the device or not. It does not check for particular application.
From the Apple Docs It returns a Boolean value indicating whether location services are enabled on the device
You may use locationManager:didFailWithError: to detect the location services for the particular app
From the Apple documentation
If the user denies your application’s use of the location service, this method reports a kCLErrorDenied error. Upon receiving such an error, you should stop the location service.
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
if ([[error domain] isEqualToString: kCLErrorDomain] && [error code] == kCLErrorDenied) {
// Location Services are denied.
}
}
You should check the authorizationStatus property of CLLocationManager to see whether your application is authorised to access location information -
if ([CLLocationManager locationServicesEnabled] && (CLLoctionManager.authorizationStatus == kCLAuthorizationStatusAuthorized)) {
// Enable button
}
else {
// Disable button
}
use this
+(BOOL)checkLocationService {
if (![CLLocationManager locationServicesEnabled])
return NO; //location service disabled
else if(kCLAuthorizationStatusAuthorized!=[CLLocationManager authorizationStatus])
return NO; //app's location service disabled
return YES;
}
For some reason the first time I open the UIImagePickerController in camera mode on my app it comes up blank. I have to close and reopen that view to get the camera feed to start working. I'm using the standard code that works in iOS 6 perfectly for camera capture. From the sample below I'm firing the capturePhoto: method. Anyone else running into this jenkiness with the iOS 7 camera? I checked the Apple dev forums but its near impossible to find answers there.
- (IBAction)capturePhoto:(id)sender {
[self doImagePickerForType:UIImagePickerControllerSourceTypeCamera];
}
- (void)doImagePickerForType:(UIImagePickerControllerSourceType)type {
if (!_imagePicker) {
_imagePicker = [[UIImagePickerController alloc] init];
_imagePicker.mediaTypes = #[(NSString*)kUTTypeImage];
_imagePicker.delegate = self;
}
_imagePicker.sourceType = type;
[self presentViewController:_imagePicker animated:YES completion:nil];
}
I'm also using UIImagePickerController and ran into the same issue with a blank screen. I'd like to expand a little on what klaudz mentioned regarding iOS 7 authorization for the camera.
Reference:
https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVCaptureDevice_Class/Reference/Reference.html
"Recording audio always requires explicit permission from the user; recording video also requires user permission on devices sold in certain regions."
Here is some code fragments you can start with to check to see if you have permission for the camera and request it if your app hadn't previously requested it. If you are denied due to an earlier request, your app may need to put up a notice to the user to go into settings to manually enable access as klaudz pointed out.
iOS 7 example
NSString *mediaType = AVMediaTypeVideo; // Or AVMediaTypeAudio
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType];
// This status is normally not visible—the AVCaptureDevice class methods for discovering devices do not return devices the user is restricted from accessing.
if(authStatus == AVAuthorizationStatusRestricted){
NSLog(#"Restricted");
}
// The user has explicitly denied permission for media capture.
else if(authStatus == AVAuthorizationStatusDenied){
NSLog(#"Denied");
}
// The user has explicitly granted permission for media capture, or explicit user permission is not necessary for the media type in question.
else if(authStatus == AVAuthorizationStatusAuthorized){
NSLog(#"Authorized");
}
// Explicit user permission is required for media capture, but the user has not yet granted or denied such permission.
else if(authStatus == AVAuthorizationStatusNotDetermined){
[AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) {
// Make sure we execute our code on the main thread so we can update the UI immediately.
//
// See documentation for ABAddressBookRequestAccessWithCompletion where it says
// "The completion handler is called on an arbitrary queue."
//
// Though there is no similar mention for requestAccessForMediaType, it appears it does
// the same thing.
//
dispatch_async(dispatch_get_main_queue(), ^{
if(granted){
// UI updates as needed
NSLog(#"Granted access to %#", mediaType);
}
else {
// UI updates as needed
NSLog(#"Not granted access to %#", mediaType);
}
});
}];
}
else {
NSLog(#"Unknown authorization status");
}
In iOS 7, an app could access the camera before getting authorize of the user.
When an app accesses the camera the first time, iOS show an alert view to ask user.
Users could also set the authorize in Settings--Privacy--Camera--[Your app's name].
The camera will stay in a black blank view if the switch is off.
If you call the camera by using AVCaptureDeviceInput, you can check like:
NSError *inputError = nil;
AVCaptureDeviceInput *captureInput =
[AVCaptureDeviceInput deviceInputWithDevice:inputDevice error:&inputError];
if (inputError &&
inputError.code == AVErrorApplicationIsNotAuthorizedToUseDevice)
{
// not authorized
}
If you call by using UIImagePickerController, I am still looking for a way to check whether got the authorize.
I tried these two methods:
[UIImagePickerController isSourceTypeAvailable:]
[UIImagePickerController isCameraDeviceAvailable:]
but they did't work that they all returned YES.
UPDATE
Thanks for Scott's expanding. [AVCaptureDevice authorizationStatusForMediaType:] is a better way to check.
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (authStatus == AVAuthorizationStatusAuthorized) {
// successful
} else {
// failed, such as
// AVAuthorizationStatusNotDetermined
// AVAuthorizationStatusRestricted
// AVAuthorizationStatusNotDetermined
}
But remember to check the iOS version, because [AVCaptureDevice authorizationStatusForMediaType:] and AVAuthorizationStatus are available above iOS 7.
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
// code for AVCaptureDevice auth checking
}
I experienced the exact same problem, and tried every solution on the Internet with no luck. But finally I found out it was the background thread prevented the camera preview to show up. If you happen to have background thread running while trying to open the camera as I do, try to block the background thread and see what happens. Hope you can get around it.
I came across this control AQPhotoPicker. It's quite easy to use, and hopefully it will help you