It happens many times some of the iPhone apps we implement to need access to kind of information imported in ways other than the user’s input, or getting data from a file or downloading from the web. A kind of that information is the contacts info existing in the Address Book of the device.
Apple allows to access the Address Book database. That happens not directly, but through a couple of provided frameworks. Even though at the bottom line it’s proved easy to fetch contacts info (if you manage to do it once), on the other hand it’s not the most straightforward and the most documented part of the iPhone programming that someone may find.
So, in this two-part tutorial, I’ll try to explain in simple words how the whole mechanism works and of course, we’ll implement a demo app. Before we begin programming, we necessarily need to understand a couple of things. Consider this as a theoretical part before going into practice. I’ll try to present it as simple as possible, just to help you get the meaning.
Everyone who owns an iPhone uses the Address Book (the Contacts) in a daily basis and in the most of the cases, more than once per day. So, I guess that we’re all familiar with it and we know how it works. That’s important (especially for the user of your app), because whenever our app needs to access the contacts info, the Address Book window is loaded (with limited functionality of course) to allow navigation through the contacts and get all the info we need for the app. That also means that you don’t need to provide a custom interface to allow user to fetch contacts info. It’s provided by the frameworks you’ll use.
There are two things that we should always keep in mind when implementing apps that use the Address Book:
- First of all, you should know that there are two ways to import contact info into an app:
- The first way is to just tap on a contact’s name and then return to your app, bringing with you all the info concerning that contact. When you’re back in your app, you may select what parts of the contact’s info you need. In this point let’s get realists, there is no way you need ALL that info, you’ll pick specific pieces of information only, except if you’re implementing your own address or phone book. Anyway, use this way only if you want to import into your app more than one pieces of information of a contact. This is what exactly we’ll discuss in this part of the tutorial.
- The second way is to tap on a contact’s name and then go to a new window, where detailed info is provided. In there, you choose what you need for your app and then go back to it. We’ll discuss this case in the Part 2 of this tutorial.
- Secondarily, every piece of information concerning a contact is a property for the Address Book framework (ex. phone property, email property, etc). To be more accurate, the Address Book framework provides the ABPerson type, which is the structure of each contact. This type contains an enumerated list of all the properties, represented as kABPersonSOMETHINGProperty (ex. kABPersonPhoneProperty, kABPersonEmailProperty, kABPersonFirstNameProperty, etc). There are various different property types. These are:
kABStringPropertyType
, for properties represented as string values.kABIntegerPropertyType
, for properties represented as integer values.kABRealPropertyType
, for properties represented as float values.kABDateTimePropertyType
, for properties represented as time or date values.
Normally, you don’t need to distinguish the kind of each property, but you need to know the type of it in order to assign it to a proper variable type. Don’t worry, when you begin typing a property in the XCode the popup window will display all the properties beginning with the letters you type, where you may see the type of each property. Some pieces of contact info may contain more than one value. For example, a contact may have more than one e-mail addresses. In that case the returned value is not a single value, but a multi-value, which also means that you don’t store these properties into a single variable but into an array (NSArray). So, there are the following types of multivalues:
kABMultiStringPropertyType
kABMultiIntegerPropertyType
kABMultiRealPropertyType
kABMultiDateTimePropertyType
One final note. After selecting contacts info to import into your app and processing it, you don’t actually store the properties (their values) directly into variables or arrays. Instead, you copy them into the variables or arrays you want, using specific methods provided by the framework. No need to worry about it, you’ll see all that in action.
In my tutorials (as well as this one), I prefer giving practical solutions to programming problems and I don’t want to get you tired with theories. For that reason, I recommend some more reading about that topic to understand even more how all that stuff work. Personally, I recommend the “The iPhone Developer’s Cookbook” by Erica Sadun and the “Address Book Programming Guide for iOS” by Apple.
Ready to go to the real work? Let’s create the demo app first.
Step 1
Begin the XCode and create a Single View Application. Click on Next and give a name. I named the app ContactsTestApp. Save it somewhere in your disk and you’re ready.
Step 2
Let’s create the interface of our app. Open the ViewController.xib
file in the Interface Builder. Add the next controls inside the view:
- A Navigation Bar at the top. Give the title “Import Contacts Demo”.
- A Bar Button Item at the right of the navigation bar. You may give it a name, I chose the “Add” Identifier (see the image below).
- An UITableView (FRAME: X:0 Y:44 W: 320 H: 416).
If you created an interface like the one below, you’re on the right path:
Step 3
We need to create and connect an outlet to the table view. We also need an array that will be used to display the imported data in the table view and of course, an IBAction
that will let us add the contacts info into the app. So inside the ViewController.h
file write these:
[objc]
@interface ViewController : UIViewController
@property (nonatomic, retain) IBOutlet UITableView *table;
@property (nonatomic, retain) NSMutableArray *contactsArray;
-(IBAction)addContact;
[/objc]
Go to the interface builder and connect the outlet to the table view. Don’t forget to connect the table’s dataSource and delegate to the File’s owner. Also, connect the IBAction to the bar button item.
Let’s also synthesize the above properties. In the ViewController.m
file write the following lines:
[objc]
@synthesize table;
@synthesize contactsArray;
[/objc]
Release them as well in the dealloc method:
[objc]
-(void)dealloc{
[table release];
[contactsArray release];
[super dealloc];
}
[/objc]
So far we’re fine. We created and set up our demo app and we’re ready to go to what we’re really interested in.
Step 4
There are two frameworks that are necessary to exist in our project in order to work with the AddressBook. These are:
- The
AddressBook.framework
- The
AddressBookUI.framework
I’m sure you know how to import frameworks into a project. Go and import the above two into this project now (select the target project –> Build Phases –> Link Binary With Libraries –> Click on the plus sign (+) and in the new window that appears search for them).
Step 5
Inside the ViewController.h file
, we need to import the appropriate libraries:
[objc]
// Import the two next necessary libraries in the ViewController.h file.
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>
[/objc]
Also, in the interface header add the following delegate just like as you see it below:
[objc]
@interface ViewController : UIViewController
[/objc]
Nice. Now, we need an object to access the address book. Again, inside the .h file:
[objc]
// The contacts object will allow us to access the device contacts.
@property (nonatomic, retain) ABPeoplePickerNavigationController *contacts;
[/objc]
At this point the .h file you have should look like this:
[objc]
#import <UIKit/UIKit.h>
// Import the two next necessary libraries in the ViewController.h file.
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>
@interface ViewController : UIViewController
@property (nonatomic, retain) IBOutlet UITableView *table;
@property (nonatomic, retain) NSMutableArray *contactsArray;
// The contacts object will allow us to access the device contacts.
@property (nonatomic, retain) ABPeoplePickerNavigationController *contacts;
-(IBAction)addContact;
@end
[/objc]
We also need to synthesize the new property and release it in the dealloc method. Go into the ViewController.m
file and add this:
[objc]
@synthesize contacts;
[/objc]
Fill the dealloc method:
[objc]
-(void)dealloc{
[table release];
[contactsArray release];
[contacts release];
[super dealloc];
}
[/objc]
Step 6
Until now, we created and setup the demo app, we imported the two necessary frameworks that are required and we also created an object of type ABPeoplePickerNavigationController
(this is actually the Address Book modal view that will show up) to access the Address Book.
Our next task is to allow the user of our super-app to add contacts into it. So, we’ll implement the IBAction we declared earlier and you connected (I hope you did) to the add button in the Interface Builder.
Inside the ViewController.m
file implement the addContact IBAction
:
[objc]
// Using this action we will allow the user to have access to device contacts.
-(IBAction)addContact{
// Init the contacts object.
contacts = [[ABPeoplePickerNavigationController alloc] init];
// Set the delegate.
[contacts setPeoplePickerDelegate:self];
// Preparation complete. Display the contacts view controller.
[self presentModalViewController:contacts animated:YES];
}
[/objc]
With the above method implemented, every time you tap (or click in the Simulator) on the add button, the Contacts modal view controller will show up, but nothing more yet. Even the Cancel button existing in the Contacts doesn’t work. Let’s work on that on the next step.
Step 7
We need to implement a couple of some delegate methods if we want the address book view controller to respond in our actions. At first, let’s make the addess book to dissappear when the user taps on the Cancel button. Inside the ViewController.m
file add the following method:
[objc]
// Implement this delegate method to make the Cancel button of the Address Book working.
-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
[contacts dismissModalViewControllerAnimated:YES];
[contacts release];
}
[/objc]
As you can see, the contacts view controller is dismissed and released.
Now it’s time for what we really want to do. We need when the user taps on a contact to fetch all its info, return back in our app and display only the data we want. In our example we’ll use these properties:
- First name
- Last name
- Phone number
- E-mail address
With the first two we’ll compose the full name. The next two are multi-values, which mean that they are returned more than one values (multiple phone numbers and multiple e-mails) and for the ease of our example we’ll get only the first values of the returned ones.
Once we fetch all the values we want, we’ll create an array with all of them. This array will be added in the contactsArray
(the contactsArray
is the array that will be used to store the data to be displayed in our table view, see later). This process is going to take place every time the user adds a contact into the app.
Add now the next delegate method into the ViewController.m
file. The comments in it explain each step. Note that we actually copy the returned contact info into our variables instead of storing them directly.
[objc]
-(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person{
// Get the first and the last name. Actually, copy their values using the person object and the appropriate
// properties into two string variables equivalently.
// Watch out the ABRecordCopyValue method below. Also, notice that we cast to NSString *.
NSString *firstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *lastName = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
// Compose the full name.
NSString *fullName = @"";
// Before adding the first and the last name in the fullName string make sure that these values are filled in.
if (firstName != nil) {
fullName = [fullName stringByAppendingString:firstName];
}
if (lastName != nil) {
fullName = [fullName stringByAppendingString:@" "];
fullName = [fullName stringByAppendingString:lastName];
}
// The phone numbers and the e-mails are contact info that have multiple values.
// For that reason we need to get them as arrays and not as single values as we did with the names above.
// Watch out the ABMultiValueCopyArrayOfAllValues method that we use to copy the necessary data into our arrays.
NSArray *phones = (NSArray *)ABMultiValueCopyArrayOfAllValues(ABRecordCopyValue(person, kABPersonPhoneProperty));
NSArray *emails = (NSArray *)ABMultiValueCopyArrayOfAllValues(ABRecordCopyValue(person, kABPersonEmailProperty));
// Create a temp array in which we’ll add all the desired values.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
[tempArray addObject:fullName];
// Make sure that the selected contact has one phone at least filled in.
if ([phones count] > 0) {
// We’ll use the first phone number only here.
// In a real app, it’s up to you to play around with the returned values and pick the necessary value.
[tempArray addObject:[phones objectAtIndex:0]];
}
else{
[tempArray addObject:@"No phone number was set."];
}
// Do the same for the e-mails.
// Make sure that the selected contact has one email at least filled in.
if ([emails count] > 0) {
// We’ll use the first email only here.
[tempArray addObject:[emails objectAtIndex:0]];
}
else{
[tempArray addObject:@"No e-mail was set."];
}
// Now add the tempArray into the contactsArray.
[contactsArray addObject:tempArray];
// Release the tempArray.
[tempArray release];
// Reload the table to display the new data.
[table reloadData];
// Dismiss the contacts view controller.
[contacts dismissModalViewControllerAnimated:YES];
[contacts release];
return NO;
}
[/objc]
In the above method, did you see the return NO command at the end? That NO (false) means that the Contacts view controller should not continue into the selected contact’s details and should return in our app. If the method returns YES, then the view controller opens a new window with the contact’s details displayed on-screen. You’ll see this case in the second part of this tutorial. Also note that we manually dismiss the contacts view controller.
For the time being everything works fine, but nothing appears on the table view yet. In the next step we make our table view fully working.
Step 8
In our table, we’ll display the full name in the textLabel area of the cell and the phone number and e-mail in the detailTextLabel
area. Note that we init each cell with the UITableViewCellStyleSubtitle
parameter. Copy the next table view delegate methods into your code:
[objc]
// Customize the number of sections in the table view.
– (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
– (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [contactsArray count];
}
// Customize the appearance of table view cells.
– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
// Make sure to change the default style to the next one if you want to manage to display text
// in the detailsTextLabel area.
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// Specify the current row.
int currentRow = [indexPath row];
// Display the selected contact’s info.
// First the full name, then the phone number and finally the e-mail address.
[[cell textLabel] setText:[[contactsArray objectAtIndex:currentRow] objectAtIndex:0]];
// The rest info will be displayed in the detailsTextLabel of the cell.
// Set the total number of lines to two (2).
[[cell detailTextLabel] setNumberOfLines:2];
[[cell detailTextLabel] setText:[NSString stringWithFormat:@"%@\n%@", [[contactsArray objectAtIndex:currentRow] objectAtIndex:1],
[[contactsArray objectAtIndex:currentRow] objectAtIndex:2]]];
return cell;
}
– (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 60.0;
}
[/objc]
Everything is ready. You may try the app in the Simulator.
Below I give some screenshots of our app. I hope this how-to helped you to get the general idea when you want to work with the Address Book. Also, if you wish or if you need to, look at the second part of this tutorial, where we’ll get only a specific value from a contact inside the Address Book.
This tutorial was transferred as it was originally written in my blog, therefore some techniques, tools or SDKs may have changed since then.