Mastering Contacts in iOS Development with Swift

Lee young-jun
4 min readDec 8, 2023

--

One of my apps involves converting user contacts. So, how do we access and select contacts in iOS?

let contactStore = CNContactStore();

Permission

To access contacts, you first need to obtain permission. This is straightforward, similar to other permission requests. Just call requestAccess.

self.contactStore.requestAccess(for: .contacts) { (result, error) in
if let error = error{
//emit error not to allow to access contacts
return;
}

if result{
observer.onNext(result);
observer.onCompleted(); //leesam
}else{
let err = NSError.init(domain: "contact.access.permission", code: -1, userInfo: ["message": "no permission"]);
Crashlytics.crashlytics().record(error: err);
observer.onNext(result);
observer.onError(err); //no permission?
}
}

Before making the requestAccess call, ensure that you've added the necessary description to info.plist.

The result of the completion indicates whether the permission is granted or not. If the result is true, the permission is granted.

If you want to know why the permission is denied, you can call authorizationStatus. However, I won't cover that in this article.

Predicate

Now that you have permission, how can you retrieve contacts?
Using unifiedContacts, you can obtain contacts, but you need to provide a predicate.

A predicate is similar to the WHERE clause in SQL.

If you know the IDs of contacts to load, create a predicate with them.

let predicate = CNContact.predicateForContacts(withIdentifiers: identifiers!)

Alternatively, if you want to get all contacts, create one with the container ID.

let containerID = self.contactStore.defaultContainerIdentifier();
let predicate = CNContact.predicateForContactsInContainer(withIdentifier: containerID);

Even with a predicate, you can’t call unifiedContacts because it also requires keysToFetch.

KeyDescriptor

What is keysToFetch? It's akin to the field name in SQL, such as select name, age from ....
Perhaps you only want to retrieve the names of contacts and not other values.

I attempted to give keys like this.

keysToFetch: [.]

However, I couldn’t get any candidates.

Actually, the key is of type [CNKeyDescriptor]. Upon closer inspection, I realized it is not an enum but just a protocol!

How can we create the key descriptor?

According to Apple’s documentation, there are predefined key descriptors.

CNContactEmailAddressesKey
CNContactPhoneNumbersKey
CNContactFormatterStyle.fullName

And you can find more keys like this.

Despite the keys being of type String, we can cast them as CNKeyDescriptor.
But CNContactFormatterStyle.fullName is not a String.

To get the key for the full name, you should call this method.

CNContactFormatter.descriptorForRequiredKeys(for: .fullName)

Entitlements

If you need some confidential keys, you should get an entitlement from Apple. In my case, Note.

You need to explain why you need this field to the Apple Reviewer.

Update

The contacts queried from CNContactStore are of type CNContact. To modify this, we have to create a cloned version.

let modifiedContact = contact.mutableCopy() as? CNMutableContact

And change the field values.

modifiedContact.xxx = xxx

Lastly, save it.

let req = CNSaveRequest();

req.update(modifiedContact);
try self.contactStore.execute(req);

If you found this post helpful, please give it a round of applause 👏. Explore more iOS-related content in my other posts.

For additional insights and updates, check out my LinkedIn profile. Thank you for your support!

Feel free to adjust it according to your style and preferences!

References

--

--

No responses yet