[iphone] 프로그래밍 방식으로 연락처에 대한 액세스 요청
iOS 6으로 업데이트 한 후 iPhone 주소록에 연락처를 추가하는 코드가 더 이상 작동하지 않는 것을 확인했습니다. Apple은 이제 연락처에 액세스하기 전에 사용자 권한이 필요하기 때문에 권한 관련 문제라고 생각합니다 ( 이 문제 해결).
아래 스크린 샷과 같이 앱이 연락처에 액세스 할 수있는 권한을 자동으로 요청할 것으로 예상했지만 그렇지 않습니다. 연락처 추가 시도는 실패합니다 ABAddressBookErrorDomain error 1
.
연락처 요청 대화 상자에 대한 액세스를 프로그래밍 방식으로 시작해야합니까? 어떻게 된 거죠?
답변
에 따라 이 문서 사과의 사이트 (페이지 중간에 개인 정보 보호 아래로 스크롤), 주소록에 액세스가 부여되어야합니다 프로그래밍 방식으로 액세스 할 수 있습니다 전에. 여기에 내가 한 일이 있습니다.
#import <AddressBookUI/AddressBookUI.h>
// Request authorization to Address Book
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
if (granted) {
// First time access has been granted, add the contact
[self _addContactToAddressBook];
} else {
// User denied access
// Display an alert telling user the contact could not be added
}
});
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// The user has previously given access, add the contact
[self _addContactToAddressBook];
}
else {
// The user has previously denied access
// Send an alert telling user to change privacy setting in settings app
}
iOS 9 이상용 업데이트 :
Apple 웹 사이트에서 :
중대한
주소록 UI 프레임 워크는 iOS 9에서 더 이상 사용되지 않습니다. 대신 ContactsUI 프레임 워크에 정의 된 API를 사용하십시오. 자세한 내용은 ContactsUI를 참조하십시오.
답변
그것은 나에게 완벽한 속임수였습니다!
iOS6에서 Apple은 새로운 개인 정보 제어를 도입했으며 사용자는 각 앱에서 연락처 및 캘린더 액세스를 제어 할 수 있습니다. 따라서 코드 측면에서 권한을 요청하는 방법을 추가해야합니다. iOS5 이전 버전에서는 언제든지
ABAddressBookRef addressBook = ABAddressBookCreate();
문제없이 주소록을 가져 오려면 iOS6에서 권한이 없으면이 호출은 빈 포인터를 반환합니다. 그래서 ABAddressBookRef를 얻기 위해 메서드를 변경해야합니다.
__block BOOL accessGranted = NO;
if (ABAddressBookRequestAccessWithCompletion != NULL) { // we're on iOS 6
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
accessGranted = granted;
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_release(sema);
}
else { // we're on iOS 5 or older
accessGranted = YES;
}
if (accessGranted) {
// Do whatever you want here.
}
코드에서 세마포어는 응답까지 차단하는 데 사용되며 ABAddressBookRequestAccessWithCompletion은 앱이 이전에 요청하지 않은 경우 권한을 요청합니다. 그렇지 않으면 설정-개인 정보-연락처의 설정을 따릅니다.
출처 : http://programmerjoe.blogspot.com/2012/10/ios6-permissions-contacts.html
답변
연락처 프레임 워크의 경우 :
- (void)checkPermissionForCNContacts
{
switch ([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts])
{
case CNAuthorizationStatusNotDetermined:
{
[[[CNContactStore alloc] init] requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted == YES)
[self showCNContactPicker];
}];
}
break;
case CNAuthorizationStatusRestricted:
case CNAuthorizationStatusDenied:
// Show custom alert
break;
case CNAuthorizationStatusAuthorized:
[self showCNContactPicker];
break;
}
}
답변
ABAddressBookRef addressBook = ABAddressBookCreate();
__block BOOL accessGranted = NO;
if (ABAddressBookRequestAccessWithCompletion != NULL) { // we're on iOS 6
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
accessGranted = granted;
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
dispatch_release(sema);
}
else { // we're on iOS 5 or older
accessGranted = YES;
}
if (accessGranted) {
if(self.isContactsChanged)
{
{
self.isContactsChanged=NO;
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
ABAddressBookRegisterExternalChangeCallback(addressBook, addressBookChanged, self);
int allPeopleCount = CFArrayGetCount(allPeople);
NSMutableArray *contactArrTemp = [[NSMutableArray alloc]init];
__block int noNumberCount=1;
managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
newMoc = [[NSManagedObjectContext alloc] init];
[newMoc setPersistentStoreCoordinator:[[AppDelegate getAppDelegate] persistentStoreCoordinator]];
[self DeleteAllPhoneContact];
NSNotificationCenter *notify = [NSNotificationCenter defaultCenter];
[notify addObserver:self
selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:newMoc];
self.backgroundQueue = dispatch_queue_create("com.storephonecontacts.bgqueue", NULL);
__block NSMutableDictionary *dic;
__block NSString *strTime,*strName,*strMobile,*strEmail,*strNotes;
__block NSDate *nsDate;
dispatch_async(self.backgroundQueue, ^{
NSMutableDictionary *dict =nil;
for (int i = 0; i < allPeopleCount; i++)
{
dic = [[NSMutableDictionary alloc]init];
ABRecordRef record = CFArrayGetValueAtIndex(allPeople,i);
NSDate *date = (NSDate*)ABRecordCopyValue(record, kABPersonCreationDateProperty);
nsDate = [date retain];
NSDateFormatter *formatterTime = [[NSDateFormatter alloc] init];
[formatterTime setDateFormat:@"hh.mm"];
NSString *dateStrPhone = [formatterTime stringFromDate:date];
strTime = [dateStrPhone retain];
[formatterTime release];
NSString *name = (NSString*)ABRecordCopyValue(record, kABPersonFirstNameProperty);
if([name length]>0)
name = [name stringByAppendingString:@" "];
NSString *name1 = (NSString*)ABRecordCopyValue(record, kABPersonLastNameProperty);
if([name1 length]>0)
{
if([name length]>0)
name = [name stringByAppendingString:name1];
else
name = (NSString*)ABRecordCopyValue(record, kABPersonLastNameProperty);
}
if([name length]>0)
strName = [name retain];
else
strName = [@"noName" retain];
//to save notes
NSString *notes = (NSString*)ABRecordCopyValue(record, kABPersonNoteProperty);
if(notes == NULL){
strNotes = @"noNotes";
}
else{
strNotes = [notes retain];
}
//for image
if (!ABPersonHasImageData(record)){
}
else{
CFDataRef imageData = ABPersonCopyImageData(record);
UIImage *image = [UIImage imageWithData:(NSData *) imageData];
[dic setObject:image forKey:@"image"];
CFRelease(imageData);
}
//To set Mobile
NSMutableArray* mobileArray = [[NSMutableArray alloc] init];
ABMutableMultiValueRef multi = ABRecordCopyValue(record, kABPersonPhoneProperty);
if (ABMultiValueGetCount(multi) > 0) {
// collect all emails in array
for (CFIndex i = 0; i < ABMultiValueGetCount(multi); i++) {
CFStringRef mobileRef = ABMultiValueCopyValueAtIndex(multi, i);
CFStringRef locLabel = ABMultiValueCopyLabelAtIndex(multi, i);
NSString *phoneLabel =(NSString*) ABAddressBookCopyLocalizedLabel(locLabel);
if([phoneLabel isEqualToString:@"mobile"])
[mobileArray addObject:(NSString *)mobileRef];
else if([phoneLabel isEqualToString:@"iPhone"])
[mobileArray addObject:(NSString *)mobileRef];
else if([phoneLabel isEqualToString:@"home"])
[mobileArray addObject:(NSString *)mobileRef];
else if([phoneLabel isEqualToString:@"work"])
[mobileArray addObject:(NSString *)mobileRef];
else if([phoneLabel isEqualToString:@"main"])
[mobileArray addObject:(NSString *)mobileRef];
else if([phoneLabel isEqualToString:@"other"])
[mobileArray addObject:(NSString *)mobileRef];
CFRelease(mobileRef);
CFRelease(locLabel);
}
}
CFRelease(multi);
if([mobileArray count]>0)
strMobile = [[mobileArray objectAtIndex:0]retain];
else{
NSString *str=[NSString stringWithFormat:@"noNumber%i",noNumberCount];
strMobile = [str retain];
noNumberCount++;
}
[mobileArray release];
//To set E-mail
NSMutableArray* emailArray = [[NSMutableArray alloc] init];
multi = ABRecordCopyValue(record, kABPersonEmailProperty);
if (ABMultiValueGetCount(multi) > 0) {
// collect all emails in array
for (CFIndex i = 0; i < ABMultiValueGetCount(multi); i++) {
CFStringRef emailRef = ABMultiValueCopyValueAtIndex(multi, i);
[emailArray addObject:(NSString *)emailRef];
CFRelease(emailRef);
}
}
CFRelease(multi);
if([emailArray count]>0)
strEmail = [[emailArray objectAtIndex:0]retain];
else
strEmail = [@"noemail" retain];
[emailArray release];
bool addBool = NO;
if([strName isEqualToString:@"noName"]){
if([strEmail isEqualToString:@"noemail"]){
}
else{
[dic setObject:strEmail forKey:@"name"];
addBool = YES;
}
if(addBool == NO){
if([strMobile isEqualToString:@"noNumber"]){
}
else{
[dic setObject:strMobile forKey:@"name"];
addBool = YES;
}
}
}
else{
[dic setObject:strName forKey:@"name"];
addBool = YES;
}
[dic setObject:strEmail forKey:@"email"];
[dic setObject:strMobile forKey:@"mobile"];
[dic setObject:nsDate forKey:@"date"];
[dic setObject:strTime forKey:@"time"];
[dic setObject:strNotes forKey:@"notes"];
if(addBool == YES)
[contactArrTemp addObject:dic];
if([strMobile hasPrefix:@"0"]){
NSString *contactNumber=[strMobile stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:@""];
if(contactNumber.length>7)
[dic setObject:@"iPhone" forKey:@"ContactType"];
}
else {
if(strMobile.length>9)
[dic setObject:@"iPhone" forKey:@"ContactType"];
}
if(![[dic objectForKey:@"ContactType"] isKindOfClass:[NSNull class]] && [dic objectForKey:@"ContactType"])
{
[self InsertContactWithContactInfoDictionary:dic];
}
[strName release];
[nsDate release];
[strEmail release];
[strMobile release];
[strTime release];
[strNotes release];
[dic release];
}
dispatch_async(self.backgroundQueue, ^(void){ [self gcdDidFinishaddfebriteParsing:dict]; });
dispatch_release(self.backgroundQueue);
self.backgroundQueue=nil;
});
}
}
else
{
[[NSNotificationCenter defaultCenter] postNotificationName:@"PhoneContactsSaved" object:nil userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:@"Successful"]];
[[NSNotificationCenter defaultCenter] postNotificationName:@"updateContacts" object:nil userInfo:[NSDictionary dictionaryWithObject:@"success" forKey:@"update"]];
}
}
답변
몇 가지 문제가 있었다 yunas Xcode5에서 iOS6.1에 코드입니다. 약간의 적응으로 그것은 나를 위해 일했습니다.
문제는 iOS 6의 ARC가 허용되지 않는 dispatch_release(sema);
작업 코드입니다. 참고 : m_addressbook
대신 addressbook
ABAddressBookRef로 사용 합니다 !
ViewController.m
#import "ViewController.h"
#import <AddressBook/AddressBook.h>
#import <AddressBook/ABAddressBook.h>
#import <AddressBook/ABPerson.h>
@interface ViewController ()
@property (nonatomic, strong) NSMutableArray* contactList;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
ABAddressBookRef m_addressbook = ABAddressBookCreateWithOptions(NULL, NULL);
__block BOOL accessGranted = NO;
if (ABAddressBookRequestAccessWithCompletion != NULL) { // we're on iOS 6
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@autoreleasepool {
// Write your code here...
// Fetch data from SQLite DB
}
});
ABAddressBookRequestAccessWithCompletion(m_addressbook, ^(bool granted, CFErrorRef error) {
accessGranted = granted;
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
else { // we're on iOS 5 or older
accessGranted = YES;
}
if (accessGranted) {
// do your stuff
}
}
// ...
답변
- (void)viewDidLoad
{
[super viewDidLoad];
[self loadPhoneContacts];
}
-(void)loadPhoneContacts{
ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
if (status == kABAuthorizationStatusDenied) {
// if you got here, user had previously denied/revoked permission for your
// app to access the contacts, and all you can do is handle this gracefully,
// perhaps telling the user that they have to go to settings to grant access
// to contacts
[[[UIAlertView alloc] initWithTitle:nil message:@"This app requires access to your contacts to function properly. Please visit to the \"Privacy\" section in the iPhone Settings app." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
return;
}
CFErrorRef error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
if (error) {
NSLog(@"ABAddressBookCreateWithOptions error: %@", CFBridgingRelease(error));
if (addressBook) CFRelease(addressBook);
return;
}
if (status == kABAuthorizationStatusNotDetermined) {
// present the user the UI that requests permission to contacts ...
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
if (error) {
NSLog(@"ABAddressBookRequestAccessWithCompletion error: %@", CFBridgingRelease(error));
}
if (granted) {
// if they gave you permission, then just carry on
[self listPeopleInAddressBook:addressBook];
} else {
// however, if they didn't give you permission, handle it gracefully, for example...
dispatch_async(dispatch_get_main_queue(), ^{
// BTW, this is not on the main thread, so dispatch UI updates back to the main queue
[[[UIAlertView alloc] initWithTitle:nil message:@"This app requires access to your contacts to function properly. Please visit to the \"Privacy\" section in the iPhone Settings app." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
});
}
if (addressBook) CFRelease(addressBook);
});
} else if (status == kABAuthorizationStatusAuthorized) {
[self listPeopleInAddressBook:addressBook];
if (addressBook) CFRelease(addressBook);
}
}
- (void)listPeopleInAddressBook:(ABAddressBookRef)addressBook
{
NSInteger numberOfPeople = ABAddressBookGetPersonCount(addressBook);
NSArray *allPeople = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBook));
for (NSInteger i = 0; i < numberOfPeople; i++) {
ABRecordRef person = (__bridge ABRecordRef)allPeople[i];
NSString *firstName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
NSString *lastName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty));
NSLog(@"Name:%@ %@", firstName, lastName);
ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
CFIndex numberOfPhoneNumbers = ABMultiValueGetCount(phoneNumbers);
for (CFIndex i = 0; i < numberOfPhoneNumbers; i++) {
NSString *phoneNumber = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phoneNumbers, i));
NSLog(@" phone:%@", phoneNumber);
}
CFRelease(phoneNumbers);
NSLog(@"=============================================");
}
}
답변
누구든지 iOS5의 주소록에 문제가 있으면 다음을 사용하십시오.
ABAddressBookRef addressBook = ABAddressBookCreate();
대신해서
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL,NULL);