When I use the Contacts Framework with Swift for iOS 15 using Xcode 13, when I use CNContactStore instance method unifiedContacts(matching:keysToFetch:), what happens to those CNContact objects that are not linked with any other CNContact objects, would they be returned as non-unified contacts, or would they be left out since they are non-unified contacts?
NOTE: I made an error in the original post before I fixed it. I meant to ask about unifiedContacts (with an "s").
Instead I accidentally wrote unifiedContact (without the "s").
The sample iOS Swift project ManagingContacts, uses both enumerateContacts(with:usingBlock:) and unifiedContacts(withIdentifier:keysToFetch:). Understanding why they choose to use each when they do may help answer the question.
The name "unifiedContacts" suggests it only fetches unified contacts, and does not fetch non-unified contacts that otherwise would meet the specified criteria, but it doesn’t make sense to me why there would ever be a need for it or why it is used in the said project when it is used.
From my test code, it looks like they both do the same thing. What do you think about this?
I tried this code in a test project:
class ViewController: UIViewController {
let store = CNContactStore()
let keys = [CNContactGivenNameKey as CNKeyDescriptor]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
store.requestAccess(for: .contacts) {
permissionGranted, error in
let request = CNContactFetchRequest(keysToFetch: self.keys)
print("enumerate contacts")
do {
try self.store.enumerateContacts(with: request) {
contact, pointer in
print(contact.givenName)
}
} catch {
print("error")
print(error.localizedDescription)
}
print("unified contacts")
do {
let containers = try self.store.containers(matching: nil)
if containers.count > 1 {
fatalError("More than one container!")
}
let predicate = CNContact.predicateForContactsInContainer(withIdentifier: self.store.defaultContainerIdentifier())
let contacts = try self.store.unifiedContacts(matching: predicate, keysToFetch: self.keys)
for contact in contacts {
print(contact.givenName)
}
} catch {
print("error")
print(error.localizedDescription)
}
}
}
}
Here’s the result in the debug window:
enumerate contacts
Kate
Daniel
John
Anna
Hank
David
unified contacts
Kate
Daniel
John
Anna
Hank
David
2
Answers
enumerateContacts
will go over every contact that matches the fetch request that you pass to it. The block will be called once for each contact.unifiedContact
is a function that receives the identifier of a contact and, if there are multiple cards for that contact, will give you a new contact that aggregates the values of all of those cards. If there is a single card then that’s what will be returned.You would probably run
enumeracteContacts
and then within the enumeration block you would callunifiedContact
to make sure you get all the relevant values for that contact.Edit:
In your updated question you’re actually using a different
unifiedContact
function than the one in your original question. This function returns an array of unified contacts.Your examples look the same because that is a basic address book and no contacts are linked. When you link two or more contacts you could have more than one contact card for a single contact, and
unifiedContacts
functions will return a single one with all the values.By default the Contacts framework returns unified contacts. You are absolutely right. The name "unifiedContacts" suggests it only fetches unified contacts, and does not fetch non-unified contacts… Other methods should be used to retrieve non-unified contacts.