in Frameworks/AddressBook/ABRecord.mm [165:549]
bool ABRecordSetValue(ABRecordRef record, ABPropertyID contactProperty, CFTypeRef value, CFErrorRef* error) {
if (record == nullptr) {
if (error) {
NSDictionary* userInfo = @{
NSLocalizedDescriptionKey : NSLocalizedString(@"Error setting record value.\n", nil),
NSLocalizedFailureReasonErrorKey : NSLocalizedString(@"The record should not be null!\n", nil)
};
*error = (__bridge_retained CFErrorRef)[NSError errorWithDomain:(__bridge NSString*)ABAddressBookErrorDomain
code:kABOperationNotPermittedByStoreError
userInfo:userInfo];
}
return false;
}
_ABContact* person = (__bridge _ABContact*)record;
if (person.type == kAddressBookReadOnlyContact) {
if (error) {
NSDictionary* userInfo = @{
NSLocalizedDescriptionKey : NSLocalizedString(@"Error setting record value.\n", nil),
NSLocalizedFailureReasonErrorKey : NSLocalizedString(@"You can only modify contacts created by your app!\n", nil)
};
*error = (__bridge_retained CFErrorRef)[NSError errorWithDomain:(__bridge NSString*)ABAddressBookErrorDomain
code:kABOperationNotPermittedByStoreError
userInfo:userInfo];
}
return false;
}
// Cases for various name properties.
if (contactProperty == kABPersonFirstNameProperty) {
NSString* firstName = (__bridge NSString*)value;
if (!_checkLength(firstName, kABPersonFirstNameLength, error, @"first name")) {
return false;
}
person.contact.FirstName(objcwinrt::string(firstName));
} else if (contactProperty == kABPersonLastNameProperty) {
NSString* lastName = (__bridge NSString*)value;
if (!_checkLength(lastName, kABPersonLastNameLength, error, @"last name")) {
return false;
}
person.contact.LastName(objcwinrt::string(lastName));
} else if (contactProperty == kABPersonMiddleNameProperty) {
NSString* middleName = (__bridge NSString*)value;
if (!_checkLength(middleName, kABPersonMiddleNameLength, error, @"middle name")) {
return false;
}
person.contact.MiddleName(objcwinrt::string(middleName));
} else if (contactProperty == kABPersonPrefixProperty) {
NSString* prefix = (__bridge NSString*)value;
if (!_checkLength(prefix, kABPersonPrefixLength, error, @"prefix")) {
return false;
}
person.contact.HonorificNamePrefix(objcwinrt::string(prefix));
} else if (contactProperty == kABPersonSuffixProperty) {
NSString* suffix = (__bridge NSString*)value;
if (!_checkLength(suffix, kABPersonSuffixLength, error, @"suffix")) {
return false;
}
person.contact.HonorificNameSuffix(objcwinrt::string(suffix));
} else if (contactProperty == kABPersonNicknameProperty) {
NSString* nickname = (__bridge NSString*)value;
person.contact.Nickname(objcwinrt::string(nickname));
} else if (contactProperty == kABPersonFirstNamePhoneticProperty) {
NSString* phoneticFirstName = (__bridge NSString*)value;
if (!_checkLength(phoneticFirstName, kABPersonFirstNamePhoneticLength, error, @"first name phonetic")) {
return false;
}
person.contact.YomiGivenName(objcwinrt::string(phoneticFirstName));
} else if (contactProperty == kABPersonLastNamePhoneticProperty) {
NSString* phoneticLastName = (__bridge NSString*)value;
if (!_checkLength(phoneticLastName, kABPersonLastNamePhoneticLength, error, @"last name phonetic")) {
return false;
}
person.contact.YomiFamilyName(objcwinrt::string(phoneticLastName));
// Cases for job-related properties.
} else if (contactProperty == kABPersonOrganizationProperty) {
NSString* organization = (__bridge NSString*)value;
if (!_checkLength(organization, kABPersonOrganizationLength, error, @"organization")) {
return false;
}
WFC::IVector<ContactJobInfo> jobInfo = person.contact.JobInfo();
if (jobInfo.Size() == 0) {
jobInfo.Append(ContactJobInfo());
}
ContactJobInfo job = jobInfo.GetAt(0);
job.CompanyName(objcwinrt::string(organization));
} else if (contactProperty == kABPersonJobTitleProperty) {
NSString* jobTitle = (__bridge NSString*)value;
if (!_checkLength(jobTitle, kABPersonJobTitleLength, error, @"job title")) {
return false;
}
WFC::IVector<ContactJobInfo> jobInfo = person.contact.JobInfo();
if (jobInfo.Size() == 0) {
jobInfo.Append(ContactJobInfo());
}
ContactJobInfo job = jobInfo.GetAt(0);
job.Title(objcwinrt::string(jobTitle));
} else if (contactProperty == kABPersonDepartmentProperty) {
NSString* department = (__bridge NSString*)value;
if (!_checkLength(department, kABPersonDepartmentLength, error, @"department")) {
return false;
}
WFC::IVector<ContactJobInfo> jobInfo = person.contact.JobInfo();
if (jobInfo.Size() == 0) {
jobInfo.Append(ContactJobInfo());
}
ContactJobInfo job = jobInfo.GetAt(0);
job.Department(objcwinrt::string(department));
// Case for birthday-related property.
} else if (contactProperty == kABPersonBirthdayProperty) {
WFC::IVector<ContactDate> dates = person.contact.ImportantDates();
ContactDate date = nullptr;
// Find the first date in the contact's important dates
// that is marked as a birthday, since a Windows contact stores
// all of its important dates in a list under a single property
// rather than explicitly storing the birthday.
for (const ContactDate& d : dates) {
if (d.Kind() == ContactDateKind::Birthday) {
date = d;
break;
}
}
// If no birthday was found, create a new date for it --
// otherwise, just use the existing birthday.
if (!date) {
date = ContactDate();
date.Kind(ContactDateKind::Birthday);
dates.Append(date);
}
NSDate* birthday = (__bridge NSDate*)value;
unsigned int units = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* components = [calendar components:units fromDate:birthday];
date.Year(objcwinrt::optional<int>([components year]));
date.Month(objcwinrt::optional<unsigned int>([components month]));
date.Day(objcwinrt::optional<unsigned int>([components day]));
// Case for note-related property.
} else if (contactProperty == kABPersonNoteProperty) {
NSString* note = (__bridge NSString*)value;
if (!_checkLength(note, kABPersonNoteLength, error, @"note")) {
return false;
}
person.contact.Notes(objcwinrt::string(note));
// Cases for various multi-value properties.
} else if (contactProperty == kABPersonEmailProperty) {
_ABMultiValue* multiValue = (__bridge _ABMultiValue*)value;
if (!_checkType(kABStringPropertyType, multiValue, error)) {
return false;
}
// Ensure that none of the values violate the allowed length of Windows
// Contacts properties.
for (int i = 0; i < [multiValue getCount]; i++) {
NSString* value = (__bridge_transfer NSString*)[multiValue copyValueAtIndex:i];
if (!_checkLength(value, kABPersonEmailLength, error, @"email")) {
return false;
}
}
WFC::IVector<ContactEmail> emails = person.contact.Emails();
emails.Clear();
NSDictionary* dict = @{
((__bridge NSString*)kABHomeLabel) : @(static_cast<NSInteger>(ContactEmailKind::Personal)),
((__bridge NSString*)kABWorkLabel) : @(static_cast<NSInteger>(ContactEmailKind::Work)),
((__bridge NSString*)kABOtherLabel) : @(static_cast<NSInteger>(ContactEmailKind::Other))
};
for (int i = 0; i < [multiValue getCount]; i++) {
NSString* label = (__bridge_transfer NSString*)[multiValue copyLabelAtIndex:i];
NSString* value = (__bridge_transfer NSString*)[multiValue copyValueAtIndex:i];
ContactEmail email;
email.Address(objcwinrt::string(value));
email.Kind(dict[label] == nil ? ContactEmailKind::Other : static_cast<ContactEmailKind>([dict[label] integerValue]));
emails.Append(email);
}
} else if (contactProperty == kABPersonAddressProperty) {
_ABMultiValue* multiValue = (__bridge _ABMultiValue*)value;
if (!_checkType(kABDictionaryPropertyType, multiValue, error)) {
return false;
}
// Ensure that none of the values violate the allowed length of Windows
// Contacts properties.
for (int i = 0; i < [multiValue getCount]; i++) {
NSDictionary* dict = (__bridge_transfer NSDictionary*)[multiValue copyValueAtIndex:i];
for (NSString* key in dict) {
if (!_checkLength(dict[key], kABPersonAddressLength, error, @"address")) {
return false;
}
}
}
WFC::IVector<ContactAddress> addresses = person.contact.Addresses();
addresses.Clear();
NSDictionary* dict = @{
((__bridge NSString*)kABHomeLabel) : @(static_cast<NSInteger>(ContactAddressKind::Home)),
((__bridge NSString*)kABWorkLabel) : @(static_cast<NSInteger>(ContactAddressKind::Work)),
((__bridge NSString*)kABOtherLabel) : @(static_cast<NSInteger>(ContactAddressKind::Other))
};
for (int i = 0; i < [multiValue getCount]; i++) {
NSString* label = (__bridge_transfer NSString*)[multiValue copyLabelAtIndex:i];
NSDictionary<NSString*,NSString*>* value = (__bridge_transfer NSDictionary<NSString*,NSString*>*)[multiValue copyValueAtIndex:i];
ContactAddress address;
address.Kind(dict[label] == nil ? ContactAddressKind::Other : static_cast<ContactAddressKind>([dict[label] integerValue]));
address.StreetAddress(objcwinrt::string(value[(__bridge NSString*)kABPersonAddressStreetKey]));
address.Locality(objcwinrt::string(value[(__bridge NSString*)kABPersonAddressCityKey]));
address.Region(objcwinrt::string(value[(__bridge NSString*)kABPersonAddressStateKey]));
address.PostalCode(objcwinrt::string(value[(__bridge NSString*)kABPersonAddressZIPKey]));
address.Country(objcwinrt::string(value[(__bridge NSString*)kABPersonAddressCountryKey]));
addresses.Append(address);
}
} else if (contactProperty == kABPersonDateProperty) {
_ABMultiValue* multiValue = (__bridge _ABMultiValue*)value;
if (!_checkType(kABDateTimePropertyType, multiValue, error)) {
return false;
}
// Filter importantDates so it only contains birthdays (since those should
// not be removed in this case.)
WFC::IVector<ContactDate> importantDates = person.contact.ImportantDates();
for (unsigned int i = 0; i < importantDates.Size(); ) {
if (importantDates.GetAt(i).Kind() == ContactDateKind::Birthday) {
i++;
} else {
importantDates.RemoveAt(i);
}
}
NSDictionary* dict = @{
((__bridge NSString*)kABPersonAnniversaryLabel) : @(static_cast<NSInteger>(ContactDateKind::Anniversary)),
((__bridge NSString*)kABOtherLabel) : @(static_cast<NSInteger>(ContactDateKind::Other))
};
for (int i = 0; i < [multiValue getCount]; i++) {
NSString* label = (__bridge_transfer NSString*)[multiValue copyLabelAtIndex:i];
NSDate* value = (__bridge_transfer NSDate*)[multiValue copyValueAtIndex:i];
ContactDate date;
date.Kind(dict[label] == nil ? ContactDateKind::Other : static_cast<ContactDateKind>([dict[label] integerValue]));
unsigned int units = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* components = [calendar components:units fromDate:value];
date.Year(objcwinrt::optional<int>([components year]));
date.Month(objcwinrt::optional<unsigned int>([components month]));
date.Day(objcwinrt::optional<unsigned int>([components day]));
importantDates.Append(date);
}
} else if (contactProperty == kABPersonPhoneProperty) {
_ABMultiValue* multiValue = (__bridge _ABMultiValue*)value;
if (!_checkType(kABStringPropertyType, multiValue, error)) {
return false;
}
// Ensure that none of the values violate the allowed length of Windows
// Contacts properties.
for (int i = 0; i < [multiValue getCount]; i++) {
NSString* value = (__bridge_transfer NSString*)[multiValue copyValueAtIndex:i];
if (!_checkLength(value, kABPersonPhoneLength, error, @"phone")) {
return false;
}
}
WFC::IVector<ContactPhone> phones = person.contact.Phones();
phones.Clear();
NSDictionary* dict = @{
((__bridge NSString*)kABHomeLabel) : @(static_cast<NSInteger>(ContactPhoneKind::Home)),
((__bridge NSString*)kABPersonPhoneMobileLabel) : @(static_cast<NSInteger>(ContactPhoneKind::Mobile)),
((__bridge NSString*)kABWorkLabel) : @(static_cast<NSInteger>(ContactPhoneKind::Work)),
((__bridge NSString*)kABPersonPhonePagerLabel) : @(static_cast<NSInteger>(ContactPhoneKind::Pager)),
((__bridge NSString*)kABPersonPhoneWorkFAXLabel) : @(static_cast<NSInteger>(ContactPhoneKind::BusinessFax)),
((__bridge NSString*)kABPersonPhoneHomeFAXLabel) : @(static_cast<NSInteger>(ContactPhoneKind::HomeFax)),
((__bridge NSString*)kABPersonPhoneCompanyLabel) : @(static_cast<NSInteger>(ContactPhoneKind::Company)),
((__bridge NSString*)kABPersonPhoneAssistantLabel) : @(static_cast<NSInteger>(ContactPhoneKind::Assistant)),
((__bridge NSString*)kABPersonPhoneRadioLabel) : @(static_cast<NSInteger>(ContactPhoneKind::Radio)),
((__bridge NSString*)kABOtherLabel) : @(static_cast<NSInteger>(ContactPhoneKind::Other))
};
for (int i = 0; i < [multiValue getCount]; i++) {
NSString* label = (__bridge_transfer NSString*)[multiValue copyLabelAtIndex:i];
NSString* value = (__bridge_transfer NSString*)[multiValue copyValueAtIndex:i];
ContactPhone phone;
phone.Number(objcwinrt::string(value));
phone.Kind(dict[label] == nil ? ContactPhoneKind::Other : static_cast<ContactPhoneKind>([dict[label] integerValue]));
phones.Append(phone);
}
} else if (contactProperty == kABPersonURLProperty) {
_ABMultiValue* multiValue = (__bridge _ABMultiValue*)value;
if (!_checkType(kABStringPropertyType, multiValue, error)) {
return false;
}
WFC::IVector<ContactWebsite> websites = person.contact.Websites();
websites.Clear();
for (int i = 0; i < [multiValue getCount]; i++) {
NSString* value = (__bridge_transfer NSString*)[multiValue copyValueAtIndex:i];
ContactWebsite website;
website.RawValue(objcwinrt::string(value));
websites.Append(website);
}
} else if (contactProperty == kABPersonRelatedNamesProperty) {
_ABMultiValue* multiValue = (__bridge _ABMultiValue*)value;
if (!_checkType(kABStringPropertyType, multiValue, error)) {
return false;
}
// Ensure that none of the values violate the allowed length of Windows
// Contacts properties.
for (int i = 0; i < [multiValue getCount]; i++) {
NSString* value = (__bridge_transfer NSString*)[multiValue copyValueAtIndex:i];
if (!_checkLength(value, kABPersonRelatedNamesLength, error, @"related name")) {
return false;
}
}
WFC::IVector<ContactSignificantOther> significantOthers = person.contact.SignificantOthers();
significantOthers.Clear();
NSDictionary* dict = @{
((__bridge NSString*)kABPersonSpouseLabel) : @(static_cast<NSInteger>(ContactRelationship::Spouse)),
((__bridge NSString*)kABPersonPartnerLabel) : @(static_cast<NSInteger>(ContactRelationship::Partner)),
((__bridge NSString*)kABPersonSiblingLabel) : @(static_cast<NSInteger>(ContactRelationship::Sibling)),
((__bridge NSString*)kABPersonParentLabel) : @(static_cast<NSInteger>(ContactRelationship::Parent)),
((__bridge NSString*)kABPersonChildLabel) : @(static_cast<NSInteger>(ContactRelationship::Child)),
((__bridge NSString*)kABOtherLabel) : @(static_cast<NSInteger>(ContactRelationship::Other))
};
for (int i = 0; i < [multiValue getCount]; i++) {
NSString* label = (__bridge_transfer NSString*)[multiValue copyLabelAtIndex:i];
NSString* value = (__bridge_transfer NSString*)[multiValue copyValueAtIndex:i];
ContactSignificantOther significantOther;
significantOther.Name(objcwinrt::string(value));
significantOther.Relationship(dict[label] == nil ? ContactRelationship::Other : static_cast<ContactRelationship>([dict[label] integerValue]));
significantOthers.Append(significantOther);
}
} else {
// No matching property was found.
if (error) {
NSDictionary* userInfo = @{
NSLocalizedDescriptionKey : NSLocalizedString(@"Error setting record value.\n", nil),
NSLocalizedFailureReasonErrorKey : NSLocalizedString(@"The property id was not recognized or supported!\n", nil)
};
*error = (__bridge_retained CFErrorRef)[NSError errorWithDomain:(__bridge NSString*)ABAddressBookErrorDomain
code:kABOperationNotPermittedByStoreError
userInfo:userInfo];
}
return false;
}
_updateManager(person);
return true;
}