I am developing an App that stores contacts in the address book . I would like to add notes field in my implementation , I know that to add a phone number this is the code to be used :
ABMultiValueRef multiPhone = ABMultiValueCreateMutable(kABMultiRealPropertyType);
ABMultiValueAddValueAndLabel(multiPhone, (__bridge_retained CFStringRef)Tel, kABWorkLabel, NULL);
ABMultiValueAddValueAndLabel(multiPhone, (__bridge_retained CFStringRef)Fax, kABPersonPhoneWorkFAXLabel, NULL);
ABRecordSetValue(contact, kABPersonPhoneProperty, multiPhone, nil);
CFRelease(multiPhone);
I know that the equivalent of kABPersonPhoneProperty is kABNoteProperty but what is the equivalent of kABWorkLabel for the note field?
Thanks
The note property, identified by kABNoteProperty, is a single-value property, not a multi-value property, so there's no corresponding label. The phone property is multi-value: it can contain several different values at the same time, so you need labels to distinguish the values. The note property is like the first name or last name property -- these can only have one value at a time. Use ABRecordSetValue() for this:
bool ABRecordSetValue (
ABRecordRef record,
ABPropertyID property,
CFTypeRef value,
CFErrorRef *error
);
There is no equivalent of kABWorkLabel for kABNoteProperty, because the note property is a single string, not a multivalue.
CFErrorRef error;
if (!ABRecordSetValue(contact, kABNoteProperty, (__bridge CFStringRef)#"hello world", &error)) {
// handle error
}
Related
In a simple methods to delete Certs by CN (the certs have previously been put there by SecItemAdd from a PKCS12 import); I am getting the error:
Property list invalid for format: 200 (property lists cannot contain objects of type 'SecIdentity')
Where based on https://developer.apple.com/documentation/security/1395547-secitemdelete I think I am following the instruction:
To delete an item identified by a transient reference, specify the
kSecMatchItemList search key with a reference returned by using the
kSecReturnRef return type key in a previous call to the
SecItemCopyMatching or SecItemAdd functions.
to the letter. Code below:
NSDictionary * attributes;
NSString * cnString = #"/CN=foo";
attributes = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)(kSecClassIdentity), kSecClass,
cnString, kSecMatchSubjectContains,
kSecMatchLimitAll, kSecMatchLimit,
kCFBooleanTrue, kSecReturnRef,
nil];
CFArrayRef result;
status = SecItemCopyMatching((__bridge CFDictionaryRef)(attributes),
(CFTypeRef *)&result);
if (status == noErr) {
for(int i = 0; i < CFArrayGetCount(result); i++) {
SecIdentityRef item = (SecIdentityRef) CFArrayGetValueAtIndex(result, i);
NSLog(#"Item #%d: %#", i, item);
attributes = [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)(kSecClassIdentity), kSecClass,
[NSArray arrayWithObject:(__bridge id)item], kSecMatchItemList,
kSecMatchLimitOne, kSecMatchLimit,
nil];
status = SecItemDelete((__bridge CFDictionaryRef)(attributes));
if (status != noErr || status != errSecItemNotFound)
NSLog(#"Delete %d/%#failed: %ld (ignored)", i,item, status);
};
};
The output on the console is:
Item #0: <SecIdentityRef: 0xc7359ff0>
straight after the find (and if the search is widened we get an array of these).
And then from deep inside Security.dylib:
Property list invalid for format: 200 (property lists cannot contain objects of type 'SecIdentity')
To ultimately bail with:
Delete 0/<SecIdentityRef: 0xc7359ff0>failed: -50 (ignored)
What am I doing wrong?
To quote from the documentation found in the header file SecItem.h, part of the Security framework:
By default, this function deletes all items matching the specified query.
You can change this behavior by specifying one of the follow keys:
To delete an item identified by a transient reference, on iOS,
specify kSecValueRef with a item reference. On OS X, give a
kSecMatchItemList containing an item reference.
To delete an item identified by a persistent reference, on iOS,
specify kSecValuePersistentRef with a persistent reference returned by
using the kSecReturnPersistentRef key to SecItemCopyMatching or
SecItemAdd. On OSX, use kSecMatchItemList with a persistent reference
returned by using the kSecReturnPersistentRef key with
SecItemCopyMatching or SecItemAdd.
This has been fixed in the latest GM drop. Reality is now in sync with the documentation.
I am currently writing an app which should enable the user to modify the contact details (mainly numbers) in the app, and then these modifications should be reflected directly to the Address Book.
I searched thoroughly on the internet, but all the examples I found were either to load the contacts or add new contact, but nothing on modifying an existing contact.
also how can I get a list of all phone numbers of a single contact, in case he has several numbers stored.
To allow a user to edit their details directly, see Apple's documentation on displaying and editing a person record. The initial section of that says, "Set the delegate, which must adopt the ABPersonViewControllerDelegate protocol. To allow the user to edit the record, set allowsEditing to YES."
For example:
ABPersonViewController *personViewController = [[ABPersonViewController alloc] init];
personViewController.personViewDelegate = self;
personViewController.allowsEditing = YES;
Other than setting allowsEditing, the code would be exactly the same as that required to display a person's details without editing. (This code was drawn from this answer which displays a fuller example regarding deleting a contact from the address book.)
However, I see your title refers to doing so programatically. Apple's Address Book Programing Guide for iOS says, "Remember that the Address Book database is ultimately owned by the user, so applications must be careful not to make unexpected changes to it. Generally, changes should be initiated or confirmed by the user."
However, it is possible. The following example appears on p.17 of that document:
ABRecordRef aRecord = ABPersonCreate();
CFErrorRef anError = NULL;
bool didSet;
didSet = ABRecordSetValue(aRecord, kABPersonFirstNameProperty, CFSTR("Katie"), &anError);
if (!didSet) {/* Handle error here. */}
didSet = ABRecordSetValue(aRecord, kABPersonLastNameProperty, CFSTR("Bell"), &anError);
if (!didSet) {/* Handle error here. */}
CFStringRef firstName, lastName;
firstName = ABRecordCopyValue(aRecord, kABPersonFirstNameProperty);
lastName = ABRecordCopyValue(aRecord, kABPersonLastNameProperty);
/* ... Do something with firstName and lastName. ... */
CFRelease(aRecord);
CFRelease(firstName);
CFRelease(lastName);
I want fetch all phone numbers from address book and i want to store in one array.
For that am writing this code,but am getting this error at second line Program received signal: “EXC_BAD_ACCESS”
ABRecordRef person;
ABMultiValueRef phoneNumberProperty = ABRecordCopyValue(person, kABPersonPhoneProperty);
NSArray* phoneNumbers = (NSArray*)ABMultiValueCopyArrayOfAllValues(phoneNumberProperty);
// CFRelease(phoneNUmberProperty);
NSLog(#"Phone numbers = %#", phoneNumbers);
[phoneNumbers release];
please help me what are changes can i do.
Thanks in advance
ABRecordCopyValue will not get you all the phone numbers of a address book. You can use this to look up a property of 1 person. Use ABAddressBookCopyArrayOfAllPeople to get all the persons from the address book first.
Is there any way to edit a contact programmatically in the iPhone? For example, I want to modify a contact which is returned by ABPeoplePickerNavigationController. How can I do this? I have:
(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
NSString *phoneNum = #"01234567890";
ABAddressBookRef addressBook = ABAddressBookCreate();
contact = ABAddressBookGetPersonWithRecordID(addressBook, ABRecordGetRecordID(person));
if(phoneNum && contact != NULL)
{
ABMutableMultiValueRef phoneNumberMultiValue = ABMultiValueCreateMutable(kABMultiStringPropertyType);
ABMultiValueAddValueAndLabel(phoneNumberMultiValue, phoneNum, kABPersonPhoneMobileLabel, NULL);
[self dismissModalViewControllerAnimated:YES];
ABRecordSetValue(contact, kABPersonPhoneProperty, phoneNumberMultiValue, nil);
//THIS CODE IS NOT BEHAVING AS I EXPECTED,
//if(ABAddressBookHasUnsavedChanges(addressBook))//Anyway this needs to be checked
{
ABAddressBookSave(addressBook, NULL);
}
}
return NO;
}
The call to ABAddressBookSave(addressBook, NULL); is working. But the problem is, all the old information of the "contact" is removed from the address book and only the "phoneNum" is saved at the end.
How can I solve this problem?
Apologies for the previous answer - I read the question wrong...! I thought that the contact wasn't saving at all on the address book.
It seems that the only information lost is the contact's other phone numbers, correct? Meaning that the ABRecordRef's other details, such as address, notes, birthdates, etc. remain intact.
If this is correct, I assume that the only feasible way to counter this deletion of other phone numbers is to simply append a PhoneLabel to the contact's existing kABPersonPhoneProperty. Instead of creating a new ABMutableMultiValueRef for the phone numbers, copy it from the record like so:
ABMutableMultiValueRef phoneNumberMultiValue = ABMultiValueCreateMutableCopy((ABMultiValueRef)ABRecordCopyValue(contact, kABPersonPhoneProperty));
With this, you can now append the new phone number with the same code:
ABMultiValueAddValueAndLabel(phoneNumberMultiValue, phoneNum, kABPersonPhoneMobileLabel, NULL);
What's neat about this is that no replacing or deletion occurs - the app simply adds another phone number to the contact's phone numbers property. :D
Hope this helps.
The first thing you should do is pass in a reference to a CFErrorRef as the second argument to ABAddressBookSave; it may be that it's trying to report an error, but unable to, since you don't give it this argument.
Currently there is no way to EDIT and SAVE the contact to the address book. The IPhone API does not support this requirement. In order to EDIT a specific contact we need to get a reference of that contact by record ID and copy all the information to the reference and then save that contact to the address book.
I am showing an addressbook view to the user and letting them click on a contact and select a phone number. If they select a phone number, I want to get the phone number as an integer and the contact's name as an NSString.
I've tried doing it with the following code:
//printf("%s\n",[[(NSArray *)ABMultiValueCopyArrayOfAllValues(theProperty) objectAtIndex:identifier] UTF8String]);
//CFArrayRef *arrayString = [[(NSArray *)ABMultiValueCopyArrayOfAllValues(theProperty) objectAtIndex:identifier] UTF8String];
NSArray *arrayString = [(NSArray *)ABMultiValueCopyArrayOfAllValues(theProperty) objectAtIndex:identifier];
printf("%s\n", arrayString);
This code is inside this method:
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
And I am checking if the user selected a phone number with this code:
if (propertyType == kABStringPropertyType)
{
[self wrongSelection];
}
else if (propertyType == kABIntegerPropertyType)
{
[self wrongSelection];
}
else if (propertyType == kABRealPropertyType)
{
[self wrongSelection];
}
else if (propertyType == kABMultiStringPropertyType)
{
//This is the phone number...
I am able to get the phone number to display in the console with printf, however I can't figure out how to convert it into an integer and how to also get the contacts name even though the property selected is not a person's name.
Also, what I'm doing seems very inefficient. Are there any better ways to do this?
Edit: If I can't store them as an int, a string would be fine. I just can't figure out how to go from that array to an actual string. If I cast it or save it as a UTF8String I always get some error.
To get the property efficiently (as far as reading goes), you can do something like this in your callback method:
switch( propertyType ) {
case kABMultiStringPropertyType:
// this is the phone number, do something
break;
default:
[self wrongSelection];
break;
}
I'm not sure you actually even need to parse that, though. To get the phone number from the record you could do (again, inside your callback method):
ABMultiValueRef phoneNumberProperty = ABRecordCopyValue(person, kABPersonPhoneProperty);
NSArray* phoneNumbers = (NSArray*)ABMultiValueCopyArrayOfAllValues(phoneNumberProperty);
CFRelease(phoneNUmberProperty);
// Do whatever you want with the phone numbers
NSLog(#"Phone numbers = %#", phoneNumbers);
[phoneNumbers release];
You can't convert the phone number into an integer. Phone numbers are strings. The default entry Apple includes for itself has the number "1-800-MYAPPLE".
Also, even if all components of a phone number are digits, there is no guarantee that phone numbers in all parts of the world are actually small enough to fit inside a 64 bit value, once you factor in area codes, country codes, internal extensions, etc. Users are free to put as much as they want in there.
Another reason not to use integers - some countries use leading zeros on phone numbers, e.g. all UK numbers start with a zero (usually written 01234 567890 or 0123 4567890)!
CFStringRef cfName = ABRecordCopyCompositeName(person);
NSString *personName = [NSString stringWithString:(NSString *)cfName];
CFRelease(cfName);
ABMultiValueRef container = ABRecordCopyValue(person, property);
CFStringRef contactData = ABMultiValueCopyValueAtIndex(container, identifier);
CFRelease(container);
NSString *contactString = [NSString stringWithString:(NSString *)contactData];
CFRelease(contactData);
contactString contains the phone number selected, and personName contains the person's name. As stated above, you can't necessarily convert the string to numbers generically, as it may contain alphabetic characters. However, you could write your own handler to convert alphabetic characters to numbers and strip out everything else to get a numeric string only, which you could then convert to a long (phone numbers get pretty long) .
I question the need to convert a phone number to a numeric value, though, since it may also contain other necessary characters like Pause. Also, a phone number represents a string of digits more than it represents one long number anyway, so the conceptual data format is more String than Int in any case.
Please be aware, that this code crashes in "stringWithString", if the Adressbook-Entry does not contain a name or a contacdata. cfName might be nil!
CFStringRef cfName = ABRecordCopyCompositeName(person);
NSString *personName = [NSString stringWithString:(NSString *)cfName];
CFRelease(cfName);
fix:
NSString *personName = nil;
if ((cfName = ABRecordCopyCompositeName(person)) != nil) {
personName = [NSString stringWithString:(NSString *)cfName];
CFRelease(cfName);
}