IOS 6のABAdressBookCreateWithOptions
およびABAddressBookRequestAccessWithCompletion
メソッドを理解しようとしています。
私が見つけることができたほとんどの情報は、「連絡先データへのアクセスを要求するには、ABAddressBookRequestAccessWithCompletion
関数を呼び出した後、ABAddressBookCreateWithOptions
関数を呼び出します」です。
これらの方法を組み合わせて、アプリケーションに連絡先へのアクセスを許可するかどうかを判断するようユーザーに警告する必要があると思いますが、それらを使用するとプロンプトが表示されません。
誰かが実際の例でこれらのメソッドを一緒に呼び出す方法のサンプルコードを提供できますか? (CFDictionary
)オプションを作成するにはどうすればよいですか?非推奨のABAddressBookCreate
メソッドを使用した作業コードがありますが、プライバシーの問題に対応するためにiOS 6に更新する必要があります。
ここに光を当てられる人に感謝します!
NDAが解除されたので、配列を返すメソッドを置き換える必要がある場合のこれに対する私の解決策を示します。既存のコードの一部を潜在的に書き換える準備ができています。以下のDavidのソリューションをご覧ください。
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) {
NSArray *thePeople = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addressBook);
// Do whatever you need with thePeople...
}
これが誰かを助けることを願っています...
この質問に対する私が見たほとんどの答えは、GCDで狂気の複雑なことを行い、最終的にメインスレッドをブロックするになります。それは必要はありません!
これが私が使用しているソリューションです(iOS 5およびiOS 6で動作します)。
- (void)fetchContacts:(void (^)(NSArray *contacts))success failure:(void (^)(NSError *error))failure {
if (ABAddressBookRequestAccessWithCompletion) {
// on iOS 6
CFErrorRef err;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &err);
if (err) {
// handle error
CFRelease(err);
return;
}
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
// ABAddressBook doesn't gaurantee execution of this block on main thread, but we want our callbacks to be
dispatch_async(dispatch_get_main_queue(), ^{
if (!granted) {
failure((__bridge NSError *)error);
} else {
readAddressBookContacts(addressBook, success);
}
CFRelease(addressBook);
});
});
} else {
// on iOS < 6
ABAddressBookRef addressBook = ABAddressBookCreate();
readAddressBookContacts(addressBook, success);
CFRelease(addressBook);
}
}
static void readAddressBookContacts(ABAddressBookRef addressBook, void (^completion)(NSArray *contacts)) {
// do stuff with addressBook
NSArray *contacts = @[];
completion(contacts);
}
他の上位の回答には問題があります:
これが私のMRCの見解です。
ABAddressBookRef ab = NULL;
// ABAddressBookCreateWithOptions is iOS 6 and up.
if (&ABAddressBookCreateWithOptions) {
NSError *error = nil;
ab = ABAddressBookCreateWithOptions(NULL, (CFErrorRef *)&error);
#if DEBUG
if (error) { NSLog(@"%@", error); }
#endif
if (error) { CFRelease((CFErrorRef *) error); error = nil; }
}
if (ab == NULL) {
ab = ABAddressBookCreate();
}
if (ab) {
// ABAddressBookRequestAccessWithCompletion is iOS 6 and up.
if (&ABAddressBookRequestAccessWithCompletion) {
ABAddressBookRequestAccessWithCompletion(ab,
^(bool granted, CFErrorRef error) {
if (granted) {
// constructInThread: will CFRelease ab.
[NSThread detachNewThreadSelector:@selector(constructInThread:)
toTarget:self
withObject:ab];
} else {
CFRelease(ab);
// Ignore the error
}
// CFErrorRef should be owned by caller, so don't Release it.
});
} else {
// constructInThread: will CFRelease ab.
[NSThread detachNewThreadSelector:@selector(constructInThread:)
toTarget:self
withObject:ab];
}
}
}
これは元の質問と周辺的に関連していますが、他のどこかで言及されているのを見たことがなく、それを理解するのに約2日かかりました。アドレス帳の変更のためにコールバックを登録する場合、それはメインスレッド上になければなりません。
たとえば、このコードでは、sync_address_book_two()のみが呼び出されます。
ABAddressBookRequestAccessWithCompletion(_addressBook, ^(bool granted, CFErrorRef error) {
if (granted) {
ABAddressBookRegisterExternalChangeCallback (_addressBook, sync_address_book_one, NULL);
dispatch_async(dispatch_get_main_queue(), ^{
ABAddressBookRegisterExternalChangeCallback (_addressBook, sync_address_book_two, NULL);
});
}
});