I have implemented EKEventStore, EKCalendar in my app to make the EKEvent within the app. It is working fine until or unless I didn't change permissions from the settings. But when I changed the requested access (on/off or off/on) from the settings, the app get crashed. I am unable to find the error. If someone has idea then please help me out. Here is the code I have implemented :
self.eventStore = [[EKEventStore alloc] init];
[self checkEventStoreAccessForCalendar];
-(void)checkEventStoreAccessForCalendar
{
EKAuthorizationStatus status = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
switch (status)
{
case EKAuthorizationStatusAuthorized: [self accessGrantedForCalendar];
break;
case EKAuthorizationStatusNotDetermined: [self requestCalendarAccess];
break;
case EKAuthorizationStatusDenied:
case EKAuthorizationStatusRestricted:
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Privacy Warning" message:#"Permission was not granted for Calendar"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
break;
default:
break;
} }
-(void)requestCalendarAccess
{
[self.eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted,
NSError *error)
{
if (granted)
{
dispatch_async(dispatch_get_main_queue(), ^{
});
}
}];
}
-(void)accessGrantedForCalendar
{
self.defaultCalendar = self.eventStore.defaultCalendarForNewEvents;
EKEvent *addEvent = [EKEvent eventWithEventStore:self.eventStore];
addEvent.title = [NSString stringWithFormat:#"%#", textcontainer.text];
}
#pragma mark EKEventEditViewDelegate
-(void)eventEditViewController:(EKEventEditViewController *)controller
didCompleteWithAction:(EKEventEditViewAction)action{
[self dismissViewControllerAnimated:YES completion:^
{
if (action != EKEventEditViewActionCanceled)
{
dispatch_async(dispatch_get_main_queue(), ^{
});
}
}];
}
- (EKCalendar *)eventEditViewControllerDefaultCalendarForNewEvents:(EKEventEditViewController *)controller
{
return self.defaultCalendar;
}
When your app goes to the background it should save all its states and should be prepared to get killed. When someone switches off access to calendar, the easiest way for the system to deals with that is to kill your app, and that is why its happening. If you switch back to your app by clicking 'Back to Your-App' on top, you can notice that your app will be launching from the beginning, not from where you left. Try not using the breakpoint and you will notice the change.
So its not a crash actually.
Related
I'm using the following logic to check if touchID is available on iPhone and based on the returned value, I direct the user to enroll in touchID or navigate them to setup a PIN. It works fine in the happy path, but if I have even one fingerprint enrolled but have disabled touchID option from iPhone system settings, then it still returns true and navigates user to setup touchID. If I remove all fingerprints, then it works as expected by returning false and navigating to PIN screen.
- (BOOL) isTouchIDAvailable {
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
if (![myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
NSLog(#"Touch ID checking error: %#", [authError localizedDescription]);
return NO;
}
return YES;
}
I've referred to some questions on stack and apple dev docs
Not sure what I'm missing? Appreciate any help. Thanks in advance :)
let context = LAContext()
var error: NSError?
if #available(iOS 9.0, *) {
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
//this is for success
}else{
//error.description for type error LAError.biometryLockout, .biometryNotEnrolled and other errors
}
}
it's not possible. if you have TouchID, you have touchID. But if you want, you can disabled with programaticly. Look at the below code snippet to check touchID is aviable or not.
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = [NSString stringWithFormat:#"Login With your fingerprint with : %#",username];
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:myLocalizedReasonString
reply:^(BOOL success, NSError *error) {
if (success) {
dispatch_async(dispatch_get_main_queue(), ^{
// [self performSegueWithIdentifier:#"Success" sender:nil];
[self loginWithFingerprint];
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
/*
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:error.description
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil];
[alertView show];
*/
// Rather than show a UIAlert here, use the error to determine if you should push to a keypad for PIN entry.
});
}
}];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
/*
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:authError.description
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil];
[alertView show];
*/
// Rather than show a UIAlert here, use the error to determine if you should push to a keypad for PIN entry.
});
}
- (void)touchIdIntegration{
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
[myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
localizedReason:#"Authenticate via Touch ID to unlock the app"
reply:^(BOOL success, NSError *error) {
if (success) {
dispatch_async(dispatch_get_main_queue(), ^{
[[AppManager instance].helpManager setDontShowInterstialAd:TRUE];
[[AppManager instance].cycleManager setUserAuthenticated:TRUE];
[self.delegate showAlertViewsOnServerData];
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
switch (error.code) {
case kLAErrorAuthenticationFailed:
NSLog(#"kLAErrorAuthenticationFailed");
break;
case kLAErrorTouchIDNotEnrolled:
NSLog(#"kLAErrorTouchIDNotEnrolled");
break;
case LAErrorPasscodeNotSet:
NSLog(#"LAError code ");
break;
default:
break;
}
});
}
}];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
self.m_imgFingerPrint.hidden = YES;
NSLog(#"LAError code %ld",authError.code);
NSString *alertMessage =nil;
switch (authError.code) {
case kLAErrorAuthenticationFailed:
{
alertMessage = #"kLAErrorAuthenticationFailed" ;
}
break;
case kLAErrorTouchIDNotEnrolled:
alertMessage = #"kLAErrorTouchIDNotEnrolled" ;
break;
case LAErrorPasscodeNotSet:
alertMessage = #"LAErrorPasscodeNotSet" ;
break;
default:
break;
}
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:alertMessage
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil, nil];
[alertView show];
});
}
}
I don't want this pop up each every time when user moves to this screen.Is it possible or not ?Please let me know.
please find the screen shot below:
Anyways thanks in advance.
Don't write this code viewDidLoad or viewDidAppear. Write it only when it is absolutely required to ask for touchID.
I use the following code to check and request authorization for the Camera. Problem is the following. The following scenario leads to a wrong authorization status:
User declines authorization for the first time
Terminates the app
Restarts the app
Leaves the application, grants authorization for the camera in settings app
Returns to the app
[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] will return AVAuthorizationStatusDeclined (authorized as said).
After terminating and restarting results in AVAuthorizationStatusAuthorized as it should. In this case the user leaves to settings and denies camera access the result will remain AVAuthorizationStatusAuthorized until next restart.
Any ideas what I miss here?
- (void) popCamera {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
//picker.allowsEditing = YES;
#if !(TARGET_IPHONE_SIMULATOR)
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
#endif
self.view.translatesAutoresizingMaskIntoConstraints = YES;
[self presentViewController:picker animated:YES completion:NULL];
}
- (void)camDenied
{
NSLog(#"%#", #"Denied camera access");
NSString *alertText;
NSString *alertButton;
BOOL canOpenSettings = (&UIApplicationOpenSettingsURLString != NULL);
if (canOpenSettings)
{
alertText = LSS(#"DeniedCamera1");
SDCAlertView *alert = [[SDCAlertView alloc]
initWithTitle:LSS(#"DeniedCameraTitle")
message:alertText
delegate:self
cancelButtonTitle:LSS(#"Cancel")
otherButtonTitles:LSS(#"Goto"), nil];
alert.tag = 3491832;
[alert show];
}
else
{
alertText = LSS(#"DeniedCamera2");
SDCAlertView *alert = [[SDCAlertView alloc]
initWithTitle:LSS(#"DeniedCameraTitle")
message:alertText
delegate:self
cancelButtonTitle:LSS(#"Cancel")
otherButtonTitles:nil];
alert.tag = 3491832;
[alert show];
}
}
- (IBAction) onTakePhoto:(id)sender {
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if(authStatus == AVAuthorizationStatusAuthorized)
{
[self popCamera];
}
else if(authStatus == AVAuthorizationStatusNotDetermined)
{
NSLog(#"%#", #"Camera access not determined. Ask for permission.");
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted)
{
if(granted)
{
[self popCamera];
}
else
{
[self camDenied];
}
}];
}
else if (authStatus == AVAuthorizationStatusRestricted)
{
SDCAlertView *alert = [[SDCAlertView alloc]
initWithTitle:LSS(#"RestrictCameraTitle")
message:LSS(#"RestrictCamera")
delegate:self
cancelButtonTitle:LSS(#"OK")
otherButtonTitles:nil];
}
else
{
[self camDenied];
}
}
Credits for the original code: Is there a way to ask user for Camera access after they have already denied it on iOS 8?
This seems to be the intended behaviour. If Apple would want you to react to authorization changes at runtime, there would be a notification that tells you that it changed.
But right now, there is no such notification (as far as I can see). You just call +authorizationStatusForMediaType: and it either returns a definitive status (like denied or authorized), or it returns AVAuthorizationStatusNotDetermined to tell you that you need to request authorization via requestAccessForMediaType:completionHandler:.
Unfortunately, this isn't an authoritative answer; I'm just drawing conclusions and guessing here. You might want to ask on Apple's developer forum and hope to get an answer from an Apple engineer.
I'm working on adding the LocalAuthentication framework to allow users to authenticate with a fingerprint on comparable devices. Everything works properly, but when a user is authenticated with their finger print it takes between 10-15 seconds for the proper segue to be performed, is this typical, or is there something wrong with the way I'm authenticating a user?
Code:
-(void)viewDidLoad { [super viewDidLoad]; [self.view setBackgroundColor:[NPSColor NPSBackgroundColor]];
UIBarButtonItem *anotherButton = [[UIBarButtonItem alloc] initWithTitle:#"Register" style:UIBarButtonItemStylePlain target:self action:#selector(onRegisterTapped)]; self.navigationItem.rightBarButtonItem = anotherButton;
LAContext *context = [[LAContext alloc] init];
NSError *error = nil; if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) { [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:#"Login?" reply:BOOL success, NSError *error {
if (error) {
}
if (success) {
[self performSegueWithIdentifier:#"loginSeg" sender:self];
} else {
}
}];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Your device cannot authenticate using TouchID."
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alert show];
} }
So it may be that performing a segue has to do with a view action which should mainly be performed on the main thread. I'm not too sure but it could very well be the evaluatePolicy method is performed on a background thread. If this is the case, you would want to throw your performSegue on the main thread.
To do so, use this:
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"loginSeg" sender:self];
});
For some reason, my app seems to take forever when I try to perform a segue. The mysterious loading time only appears, after I request permission for the AddressBook - if the Permission is already granted, there is no issue at all.
So, before actually using the Segue, i'm doing something like this:
Requesting Permission for Address Book
-(void)requestPermissionForContacts
{
ABAddressBookRef addressbook = ABAddressBookCreateWithOptions(nil, nil);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)
{
ABAddressBookRequestAccessWithCompletion(addressbook, ^(bool granted, CFErrorRef error) {
if (granted)
{
NSLog(#"granted");
[self didGrantPermissions];
} else{
NSLog(#"denied");
[self didNotGrantPermission];
}
});
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized)
{
[self didGrantPermissions];
} else {
[self didNotGrantPermission];
}
CFRelease(addressbook);
}
Whatever they user clicks:
-(void)didGrantPermissions
{
[self performSegueWithIdentifier:#"addressBook" sender:nil];
}
-(void)didNotGrantPermission
{
[[[UIAlertView alloc] initWithTitle:#"Address Book" message:#"To access this feature, please allow us to access your Address Book" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil] show];
}
And after that,
prepareForSegue
if ([segue.identifier isEqualToString:#"addressBook"])
{
CWAddressBookViewController *abvc = segue.destinationViewController;
abvc.delegate = self;
abvc.addressType = self.addressType;
}
If it is the first time, the App is accessing this view (so, if its still asking for permission) it takes quite a while (20sec!) until -(void)viewDidLoad is being called on CWAddressBookViewControlller
If the permission is already granted, it is performing the segue as expected in the blink of an eye.
Any idea what im doing wrong here?