Can't Access Contacts Sources on Device in iOS 6
Asked Answered
S

3

14

This code worked OK on iOS 5.1 and also does work in the iPhone simulator with iOS 6. It fails silently on my iPhone 4 running iOS 6. The end result is that I cannot add a person to the Contacts app. Neither of the following code snippets work (log follows each):

ABRecordRef defaultSource = ABAddressBookCopyDefaultSource(_addressBook);
NSLog(@"2 - defaultSource = %@", defaultSource);

AB: Could not compile statement for query (ABCCopyArrayOfAllInstancesOfClassInSourceMatchingProperties): SELECT ROWID, Name, ExternalIdentifier, Type, ConstraintsPath, ExternalModificationTag, ExternalSyncTag, AccountID, Enabled, SyncData, MeIdentifier, Capabilities FROM ABStore WHERE Enabled = ?;

2012-09-24 11:00:36.731 QR vCard[193:907] 2 - defaultSource = (CPRecord: 0x1f59fd50 ABStore)

When I try to add a person to the Address Book I get this (seems to be because the source is invalid, even though it looks like it might be OK from the above):

2012-09-24 11:18:32.231 QR vCard[220:907] ABAddressBookAddRecord error = The operation couldn’t be completed. (ABAddressBookErrorDomain error 1.)


I thought I could get all the sources and then pick one, but the following returns none at all:

CFArrayRef allSources = ABAddressBookCopyArrayOfAllSources (_addressBook);
NSLog(@"2 - allSources = %@", allSources);

AB: Could not compile statement for query (ABCCopyArrayOfAllInstancesOfClassInSourceMatchingProperties): SELECT ROWID, Name, ExternalIdentifier, Type, ConstraintsPath, ExternalModificationTag, ExternalSyncTag, AccountID, Enabled, SyncData, MeIdentifier, Capabilities FROM ABStore WHERE Enabled = ?;

2012-09-24 10:58:09.908 QR vCard[177:907] 2 - allSources = ()

Stanstance answered 24/9, 2012 at 18:31 Comment(1)
I found out that in iOS 6 you need PERMISSION from the user to add a person to the AddressBook. Apple protecting us again. Adds to the complexity of the code...Stanstance
G
24

I had the same issue and I could not get the Allow Access To Contacts alert to popup.

The Answer was posted by Kyle here: https://mcmap.net/q/102297/-programmatically-request-access-to-contacts

  // 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
    });
  }
  else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
        // The user has previously given access, add the contact
  }
  else {
        // The user has previously denied access
        // Send an alert telling user to change privacy setting in settings app
  }
Gilmour answered 3/10, 2012 at 1:52 Comment(0)
C
3

This log message is an indication that your app is not (maybe not yet) allowed to access Contacts. iOS 6 gives users the possibility to deny apps the permission to access the address book.

The message disappears once the user has allowed your app access to Contacts - either via the pop up dialog, or by going to Settings -> Privacy -> Contacts.

For more infos on this topic, see WWDC 2012 session 710 "Privacy support in iOS and OS X".

Carpetbag answered 26/9, 2012 at 15:58 Comment(0)
C
0

If you got here from Google and you're using iOS's new CNContactStore framework, and getting these errors, read on:

I thought it'd be cleaner to make my CNContactStore a member variable that was initialized with the class instance:

class foo {
    var contactStore = CNContactStore()

    func findByIdentifier(identifier: String) -> CNContact {
        let contact = try self.contactStore.unifiedContactWithIdentifier(identifier...
        return contact
    }
}

After I called this about fifty times, it started erroring out with

AB: Could not compile statement for query (ABCCopyArrayOfAllInstancesOfClassInSourceMatchingProperties)

I tried rate-limiting my calls, but that didn't help. It turned out that instantiating a new CNContactStore for every call had zero performance ramifications and completely solved the problem for me:

class foo {

    func findByIdentifier(identifier: String) -> CNContact {
        let contactStore = CNContactStore()
        let contact = try contactStore.unifiedContactWithIdentifier(identifier...
        return contact
    }
}

Hope this helps!

Crambo answered 8/4, 2016 at 17:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.