Mastering Contacts in iOS Development with Swift
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!