bool ABRecordSetValue()

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;
}