In my app, I collect data from clients like names, addresses, phone numbers etc. I'd like to offer a button for saving and updating these data to apple's contacts app.
Therefor I wrote this class:
class ContactsClass {
func addToContacts(
lastName: String,
firstName: String,
mail: String = "",
phone1: String = "",
street: String,
city: String,
postCode: String
) {
// Create a mutable object to add to the contact
let contact = CNMutableContact()
contact.givenName = firstName
contact.familyName = lastName
if mail != "" {
let homeEmail = CNLabeledValue(label: CNLabelHome, value: mail as NSString)
contact.emailAddresses = [homeEmail]
}
contact.phoneNumbers = []
if phone1 != "" {
contact.phoneNumbers.append(CNLabeledValue(label: CNLabelPhoneNumberMain, value: CNPhoneNumber(stringValue: phone1)))
}
let homeAddress = CNMutablePostalAddress()
homeAddress.street = street
homeAddress.city = city
homeAddress.postalCode = postCode
contact.postalAddresses = [CNLabeledValue(label: CNLabelHome, value: homeAddress)]
// Save the newly created contact
let store = CNContactStore()
let saveRequest = CNSaveRequest()
saveRequest.add(contact, toContainerWithIdentifier: gruppe)
do {
try store.execute(saveRequest)
} catch {
print("Saving contact failed, error: \(error)")
// Handle the error
}
}
func checkForExistingContact(familyName: String, givenName: String) -> CNContact? {
let store = CNContactStore()
do {
let predicate = CNContact.predicateForContacts(matchingName: familyName)
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactEmailAddressesKey] as [CNKeyDescriptor]
let contacts = try store.unifiedContacts(matching: predicate, keysToFetch: keysToFetch)
for contact in contacts {
if contact.givenName == givenName {
return contact
}
}
return nil
} catch {
return nil
}
}
func updateContact(
contact: CNContact,
lastName: String,
firstName: String,
mail: String = "",
phone1: String = "",
street: String,
city: String,
postCode: String
) {
let mutableContact = contact.mutableCopy() as! CNMutableContact
mutableContact.givenName = firstName
mutableContact.familyName = lastName
if mail != "" {
let homeEmail = CNLabeledValue(label: CNLabelHome, value: mail as NSString)
mutableContact.emailAddresses = [homeEmail]
}
mutableContact.phoneNumbers = []
if phone1 != "" {
mutableContact.phoneNumbers.append(CNLabeledValue(label: CNLabelPhoneNumberMain, value: CNPhoneNumber(stringValue: phone1)))
}
let homeAddress = CNMutablePostalAddress()
homeAddress.street = street
homeAddress.city = city
homeAddress.postalCode = postCode
mutableContact.postalAddresses = [CNLabeledValue(label: CNLabelHome, value: homeAddress)]
// Save the updated created contact
let store = CNContactStore()
let saveRequest = CNSaveRequest()
saveRequest.update(mutableContact)
do {
try store.execute(saveRequest)
} catch {
print("Saving contact failed, error: \(error)")
// Handle the error.
}
}
}
My button calls this func
private func addToContacts() {
let con = ContactsClass()
if let contact = con.checkForExistingContact(familyName: klient.nachname!, givenName: klient.vorname!) {
con.updateContact(
contact: contact,
lastName: klient.nachname!,
firstName: klient.vorname!,
mail: klient.email ?? "",
phone1: klient.telefon1 ?? "",
street: selectedAddress?.strasse ?? "",
city: selectedAddress?.ort ?? "",
postCode: selectedAddress?.plz ?? ""
)
} else {
con.addToContacts(
lastName: klient.nachname!,
firstName: klient.vorname!,
mail: klient.email ?? "",
phone1: klient.telefon1 ?? "",
street: selectedAddress?.strasse ?? "",
city: selectedAddress?.ort ?? "",
postCode: selectedAddress?.plz ?? ""
)
}
}
My problem:
2023-06-21 21:28:25.166437+0200 justCare 2[64058:433979] [core] Attempted to register account monitor for types client is not authorized to access: {(
"com.apple.account.CardDAV",
"com.apple.account.Exchange",
"com.apple.account.LDAP"
)}
2023-06-21 21:28:25.166546+0200 justCare 2[64058:433979] [accounts] CNAccountCollectionUpdateWatcher 0x6000020a04c0: Store registration failed: Error Domain=com.apple.accounts Code=7 "(null)"
2023-06-21 21:28:25.166605+0200 justCare 2[64058:433979] [accounts] CNAccountCollectionUpdateWatcher 0x6000020a04c0: Update event received, but store registration failed. This event will be handled, but the behavior is undefined.
2023-06-21 21:28:25.340559+0200 justCare 2[64058:433979] [api] Attempt to read notes by an unentitled app
2023-06-21 21:28:25.343112+0200 justCare 2[64058:433979] [General] A property was not requested when contact was fetched.
2023-06-21 21:28:25.348149+0200 justCare 2[64058:433979] [General] (
0 CoreFoundation 0x000000019e1d3154 __exceptionPreprocess + 176
1 libobjc.A.dylib 0x000000019dcf24d4 objc_exception_throw + 60
2 CoreFoundation 0x000000019e1d2ff8 +[NSException exceptionWithName:reason:userInfo:] + 0
3 Contacts 0x00000001b16f23c4 -[CNMutableContact setPhoneNumbers:] + 328
4 justCare 2 0x0000000100a04e04 $s10justCare_213ContactsClassC13updateContact7contact8lastName05firstI09birthDate4mail6phone16phone26mobile6street4city8postCode6gruppeySo9CNContactC_S2S10Foundation0L0VSgS8StF + 1752
5 justCare 2 0x0000000100e3eb64 $s10justCare_220KlientenAdressenViewV13addToContacts33_94E28232C5F5EF34661BF1E9CA92653DLLyyF + 6208
6 justCare 2 0x0000000100e3d314 $s10justCare_220KlientenAdressenViewV4bodyQrvg7SwiftUI05TupleE0VyAE0E0PAEE5sheet11isPresented9onDismiss7contentQrAE7BindingVySbG_yycSgqd__yctAeHRd__lFQOyAE6HStackVyAGyAE6SpacerV_AE6ButtonVyAiEE10labelStyleyQrqd__AE05LabelU0Rd__lFQOyAE0V0VyAE4TextVAE5ImageVG_AE08IconOnlyvU0VQo_GAiEE8disabledyQrSbFQOyA8__Qo_A10_SgAiEE5alert_AK7actionsQrAE18LocalizedStringKeyV_APqd__yXEtAeHRd__lFQOyA10__AGyAWyA1_G_A16_tGQo_AiEE5frame5width6height9alignmentQr12CoreGraphics7CGFloatVSg_A26_AE9AlignmentVtFQOyAE7DividerV_Qo_AiEE4helpyQrA15_FQOyA10__Qo_tGG_AA014AdresseAddEditE0VQo__AiEE0M6Change2of7performQrqd___yqd__ctSQRd__lFQOyAiEEA19_A20_A21_A22_QrA26__A26_A28_tFQOyAE4ListVyAA0cD0CAE7ForEachVyAE14FetchedResultsVyA45_G10Foundation4UUIDVSgAiEE20listRowSeparatorTint_5edgesQrAE5ColorVSg_AE12VerticalEdgeO3SetVtFQOyAiEE16listRowSeparator_A56_QrAE10VisibilityO_A63_tFQOyAiEE7paddingyQrAE4EdgeOA62_V_A26_tFQOyAiEE3tagyQrqd__SHRd__lFQOyASyAGyAiEEA19_A20_A21_A22_QrA26__A26_A28_tFQOyAE6VStackVyAGyAiEEA19_A20_A21_A22_QrA26__A26_A28_tFQOyA1__Qo_Sg_A74_A74_tGG_Qo__A73_yAGyASyAGyA1__AuiEE15foregroundColoryQrA59_FQOyA3__Qo_A80_SgA3_SgtGG_ASyAGyA1__AUA1_SgA85_tGGtGGtGG_A45_Qo__Qo__Qo__Qo_GG_Qo__AA0C0CQo_tGyXEfU_A34_yXEfU_yycfU8_ + 36
7 SwiftUI 0x00000001c6e285e4 block_destroy_helper + 1276
8 SwiftUI 0x00000001c6e26978 OUTLINED_FUNCTION_1 + 23992
9 SwiftUI 0x00000001c6e269dc OUTLINED_FUNCTION_1 + 24092
10 AppKit 0x00000001a15218a8 -[NSApplication(NSResponder) sendAction:to:from:] + 440
11 AppKit 0x00000001a15216c0 -[NSControl sendAction:to:] + 72
12 AppKit 0x00000001a1521604 __26-[NSCell _sendActionFrom:]_block_invoke + 100
13 AppKit 0x00000001a152152c -[NSCell _sendActionFrom:] + 204
14 AppKit 0x00000001a1521450 -[NSButtonCell _sendActionFrom:] + 88
15 AppKit 0x00000001a151ea54 NSControlTrackMouse + 1480
16 AppKit 0x00000001a151e460 -[NSCell trackMouse:inRect:ofView:untilMouseUp:] + 144
17 AppKit 0x00000001a151e318 -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] + 488
18 AppKit 0x00000001a151d7e4 -[NSControl mouseDown:] + 448
19 AppKit 0x00000001a151c2cc -[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 3476
20 AppKit 0x00000001a14a6f08 -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 364
21 AppKit 0x00000001a14a6bc8 -[NSWindow(NSEventRouting) sendEvent:] + 284
22 AppKit 0x00000001a14a5f0c -[NSApplication(NSEvent) sendEvent:] + 1556
23 AppKit 0x00000001a16f5fc4 -[NSApplication _handleEvent:] + 60
24 AppKit 0x00000001a136d368 -[NSApplication run] + 500
25 AppKit 0x00000001a1344794 NSApplicationMain + 880
26 SwiftUI 0x00000001c643f6b8 OUTLINED_FUNCTION_8 + 8272
27 SwiftUI 0x00000001c75a05ac OUTLINED_FUNCTION_14 + 188
28 SwiftUI 0x00000001c6e20c48 OUTLINED_FUNCTION_1 + 136
29 justCare 2 0x0000000101365e9c $s10justCare_20aB5_2AppV5$mainyyFZ + 40
30 justCare 2 0x000000010136619c main + 12
31 dyld 0x000000019dd23f28 start + 2236
)
Why isn't the contact updated? I would be very thankful for help.
The error seems to be around modifying the contact's phone number based on the stack trace.
3 Contacts 0x00000001b16f23c4 -[CNMutableContact setPhoneNumbers:] + 328
There's also the message:
A property was not requested when contact was fetched.
Looking at your code, your checkForExistingContact
method only fetches the attributes CNContactGivenNameKey
, CNContactFamilyNameKey
, and CNContactEmailAddressesKey
. The phone number key is not listed.
There is also this note in the documentation for CNMutableContact
:
You may modify only those properties whose values you fetched from the contacts database. When fetching a contact, you specify which properties you want to retrieve from the database. The contact store then populates the properties of a
CNContact
object with those values. After creating a mutable copy of that object, you can modify only those properties for which a value exists. If you attempt to access a property that is not available, theCNMutableContact
object throws aCNContactPropertyNotFetchedExceptionName
exception.
You need to update the line:
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactEmailAddressesKey] as [CNKeyDescriptor]
to include all possible keys you will try to update in your updateContact
method.