if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
if (granted) {
// First time access has been granted, add the contact
[self prepareContactsIDs];
} else {
UIAlertView *accessDenied = [[UIAlertView alloc] initWithTitle:#"Need Access to Addressbook" message:#"KeepItClean requires an access to addressbook in order to be usable" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[accessDenied show];
}
});
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// The user has previously given access, add the contact
if ([self isFirstRun]) {
[self prepareContactsIDs];
}
}
else {
// The user has previously denied access
// Send an alert telling user to change privacy setting in settings app
UIAlertView *accessDenied = [[UIAlertView alloc] initWithTitle:#"Need Access to Addressbook" message:#"KeepItClean requires an access to addressbook in order to be usable" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[accessDenied show];
}
There is something i don't understand here, when the user is prompted to give access, if he taps cancel, i don't get my 2nd UIAlertView showing up, the (accessDenied) alert view.
Also i feel there is something i don't understand that is related to dispatching and queues.
Try this
ABPeoplePickerNavigationController *peoplePicker = [[ABPeoplePickerNavigationController alloc] init];
peoplePicker.peoplePickerDelegate= self;
ABAddressBookRef UsersAddressBook = ABAddressBookCreateWithOptions(NULL, NULL);
if (ABAddressBookGetAuthorizationStatus()!= kABAuthorizationStatusDenied)
{
//Show alert of access
}
else
{
//Show alert of access denied
}
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.
});
}
I'm trying to add a contact to the address book in iOS8. Unable to do so anymore. Here's my code below:
-(void)addPersonToAddressBook {
NSString * fullName = integrationDictionary[#"fullName"];
ABPeoplePickerNavigationController *pp =[ABPeoplePickerNavigationController new];
ABAddressBookRef addressBook = [pp addressBook];
ABRecordRef entry = ABPersonCreate();
CFErrorRef cfError=nil;
ABRecordSetValue(entry, kABPersonFirstNameProperty, (__bridge CFTypeRef)(fullName) , nil);
ABAddressBookAddRecord(addressBook, entry, &cfError);
if (ABAddressBookSave(addressBook, &cfError)) {
NSString *saveMessage = [NSString stringWithFormat:#"%# has been added to your address book.", fullName];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Contact Added" message:saveMessage delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
} else {
NSString *saveMessage = [NSString stringWithFormat:#"There was an error adding %# to your address book.", fullName];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Uh Oh" message:saveMessage delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
NSLog(#"error is %#", cfError);
The error is showing up as null. Has anyone seen this before? Any workarounds?
The error is returning NULL because there's no error registered.
The problem is that [pp addressBook] is returning nil. So your ABAddressBookRef addressBook reference is nil.
The workaround is to use ABAddressBookCreateWithOptions instead of [pp addressBook] method of ABPeoplePickerNavigationController.
Here's a sample which works just fine on both iOS 7.1 & iOS 8.1:
-(void)requestAuthorizationAndAddPersonToAddressBook
{
// Request authorization to Address Book
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
// First time access has been granted, add the contact
[self addPersonToAddressBook];
});
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// The user has previously given access, add the contact
[self addPersonToAddressBook];
}
else {
// The user has previously denied access
// Send an alert telling user to change privacy setting in settings app
}
}
-(void)addPersonToAddressBook {
NSString * fullName = #"James Bond";
CFErrorRef abCreateError = nil;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &abCreateError);
if (abCreateError) {
NSLog(#"Error occurred: %#", abCreateError);
}
ABRecordRef entry = ABPersonCreate();
CFErrorRef cfError=nil;
ABRecordSetValue(entry, kABPersonFirstNameProperty, (__bridge CFTypeRef)(fullName) , nil);
ABAddressBookAddRecord(addressBook, entry, &cfError);
if (ABAddressBookSave(addressBook, &cfError)) {
NSString *saveMessage = [NSString stringWithFormat:#"%# has been added to your address book.", fullName];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Contact Added" message:saveMessage delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
} else {
NSString *saveMessage = [NSString stringWithFormat:#"There was an error adding %# to your address book.", fullName];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Uh Oh" message:saveMessage delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
if (cfError) {
NSLog(#"error is %#", cfError);
}
}
I would like my app to only ask the user for permission to access the Contacts addressbook until the user access the proper function in my app. I really don't want to request permission when the app loads.
As a result I've used the following code:
- (IBAction)importClientsButtonPressed:(id)sender {
// request access to Contacts address book
CFErrorRef addyError = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &addyError);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef addyError) {
});
}
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
_importContactsActionSheet = [[UIActionSheet alloc] initWithTitle:#"Import Client from Contacts"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:#"Primary Contact", #"Secondary Contact", nil];
_importContactsActionSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent;
[_importContactsActionSheet showFromRect:self.importClientsButton.frame inView:self.importClientsButton.superview animated:YES];
} else {
// the user has previously denied access - send alert to user to allow access in Settings app
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Privacy Settings"
message:#"This app does not have access to your contacts. You can enable access in Privacy Settings."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
However, the permission dialog does not halt the app and wait for a response...the code following the request continues to run. As a result I get a whack of messages popping up out of order.
Is there any way to have the whole app halt until a response comes back from the permission request dialog?
Thanks!
In your code above also you have this:
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef addyError) {
if(granted) {
//Put here your code
}
});
so finally i would write the code in this way:
- (IBAction)importClientsButtonPressed:(id)sender {
__weak typeof(self) weakSelf = self;
// request access to Contacts address book
CFErrorRef addyError = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &addyError);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef addyError) {
if(granted) {
[weakSelf openImportContact];
}
});
} else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
[weakSelf openImportContact];
} else {
// the user has previously denied access - send alert to user to allow access in Settings app
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Privacy Settings"
message:#"This app does not have access to your contacts. You can enable access in Privacy Settings."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
- (void)openImportContact {
dispatch_async(dispatch_get_main_queue(), ^{
_importContactsActionSheet = [[UIActionSheet alloc] initWithTitle:#"Import Client from Contacts"
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:#"Primary Contact", #"Secondary Contact", nil];
_importContactsActionSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent;
[_importContactsActionSheet showFromRect:self.importClientsButton.frame inView:self.importClientsButton.superview animated:YES];
});
}
I want to link the address book view controller in my app , I have searched the internet and found in apple developer guide but they show how to open contacts picker when clicking on a button and I don't want that I want the contacts picker be a view controller in my storyboard . I think i can do that by making a custom tableviewcontroller and getting information from address book or can I do it directly ?
This should help you get started:
Also, don't forget to import and link to the following frameworks:
#import <AddressBookUI/AddressBookUI.h>
#import <AddressBook/AddressBook.h>
Simply call: [self requestPermissionForContacts];
- (void)requestPermissionForContacts
{
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)
{
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error)
{
if (granted)
{
dispatch_async(dispatch_get_main_queue(), ^
{
[self findContacts];
});
}
else
{
dispatch_async(dispatch_get_main_queue(), ^
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Find Contacts" message:#"To allow us to find your contacts, you will need to go to the Settings app > Privacy > Contacts, and set your app name here to On." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
});
}
});
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized)
{
[self findContacts];
}
else
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Find Contacts" message:#"To allow us to find your contacts, you will need to go to the Settings app > Privacy > Contacts, and set your app name here to On." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}
- (void)findContacts
{
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
CFArrayRef people = ABAddressBookCopyArrayOfAllPeople(addressBookRef);
CFIndex numberOfPeople = CFArrayGetCount(people);
if (numberOfPeople > 0)
{
NSMutableArray *mutableEmailsArray = [[NSMutableArray alloc]init];
NSMutableArray *mutablePhonesArray = [[NSMutableArray alloc]init];
for (int i = 0; i < numberOfPeople; i++)
{
ABRecordRef ref = CFArrayGetValueAtIndex(people, i);
ABMultiValueRef emails = (__bridge ABMultiValueRef)((__bridge NSString*)ABRecordCopyValue(ref, kABPersonEmailProperty));
ABMultiValueRef phones = (__bridge ABMultiValueRef)((__bridge NSString*)ABRecordCopyValue(ref, kABPersonPhoneProperty));
if (ABMultiValueGetCount(emails) > 0)
{
[mutableEmailsArray addObjectsFromArray:((__bridge NSArray *)(ABMultiValueCopyArrayOfAllValues(emails)))];
}
if (ABMultiValueGetCount(phones) > 0)
{
[mutablePhonesArray addObjectsFromArray:((__bridge NSArray *)(ABMultiValueCopyArrayOfAllValues(phones)))];
}
}
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:nil message:[NSString stringWithFormat:#"%ld Emails %ld Phone Numbers", (unsigned long)mutableEmailsArray.count, (unsigned long)mutablePhonesArray.count] delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
else
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"No Contacts" message:#"No contacts were found on your device." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
}
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?