Last February, something happens. A very popular iOS application was found phoning home the whole address book of users. Granted, it was not the only one. Many where capturing all sort of data to phone home. Apple was not to blame here, after all, the contract forbids to gather user information without prior consent. But the policy was not really enforced. Apple took some heat after the Address Book-Gate and decided to change things in iOS 6. From now on, third party applications access to contacts, calendars, reminders and photos is mediated by the OS, and authorised by the user.
Address Book
In Address Book, the protected store is the address book ref, if you call ABAddressBookCreate, the os will perform the authorisation.
CABAddressBookRef addressBook = ABAddressBookCreate();
If the app was not previously authorised, and if the user is enabled to authorise the app, an alert view is showed with a brief explanation of what is happening.
The OS check is transparent to the application, ABAddressBookCreate is the same API used in iOS 5, and even without changing a single line of code, in iOS 6 the address book reference is protected. But even if you don’t really have to change anything, some changes are going to be greatly beneficial.
For instance, the protected APIs are going to block the thread until the user either grants or reject the authorisation. In other words, wraps all your calls in a dispatch block to allow the rest of the app to keep running.
Another thing to check is the returning value. In the past, the likelihood to get back a nil object was small. Now, if the user does not grant access to the siloed store, the function will return nil1.
Also, check whether the API you are using has been deprecated. ABAddressBookCreate for instance has been deprecated and apple recommends to replace it with ABAddressBookCreateWithOptions that takes a pointer to a CFErrorRef that will be populated with more information, if needed (oddly enough, the first argument, options is a reserved CFDictionaryRef, for now, we have to pass NULL)
CFErrorRef error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
The error can be either raised because an store problem was found (kABOperationNotPermittedByStoreError) or because the user had denied access to the database (kABOperationNotPermittedByUserError).
The first time the function is ran, it will returns, immediately, an empty read-only database. At this point you should register with ABAddressBookRegisterExternalChangeCallBack
if ( addressBook == NULL ) {
NSLog(@"%@", error);
} else {
ABAddressBookRegisterExternalChangeCallback(addressBook, addressBookChanged, (__bridge void *)(self));
}
And in your callback, update the address book reference with
void addressBookChanged(ABAddressBookRef addressBook, CFDictionaryRef dicRef, void *context) {
ABAddressBookRevert(addressBook);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople( addressBook );
CFIndex nPeople = ABAddressBookGetPersonCount( addressBook );
for ( int i = 0; i < nPeople; i++ ) {
ABRecordRef ref = CFArrayGetValueAtIndex( allPeople, i );
NSLog(@"%@", ref);
}
}
If you want, you can also check the app access status with the function ABAddressBookGetAuthorizationStatus that returns:
typedef CF_ENUM(CFIndex, ABAuthorizationStatus) {
kABAuthorizationStatusNotDetermined = 0,
kABAuthorizationStatusRestricted,
kABAuthorizationStatusDenied,
kABAuthorizationStatusAuthorized
};
kABAuthorizationStatusDenied and kABAuthorizationStatusAuthorized are self explanatory. kABAuthorizationStatusNotDetermined means that the app had never tries to gain access to the store, thus the OS does not really knows what to say. kABAuthorizationStatusRestricted is issued when the user is not allowed himself to grant access to the store (parental control might be enabled). As far as the app is concerned, kABAuthorizationStatusRestricted should be consider a no.
Testing
It is very important to note that data isolation is NOT supported on simulator. Unfortunately, only the device is supported. And since authorisation UI is only showed once, but any test have to check all the possible results, you will have to reset settings. Is going to be tiresome, but needed. And please, fail gracefully. Try as hard as possible to keep going even if the user does not grant access to some store.