Phonebook/NSTableView Tutorial for Dummies(Xcode 3.1.3)
Introduction
This tutorial explains how to create a simple phonebook with a search function which will look similar to this:

The following things will be covered by this tutorial:
- creating a window with a drawer
- feeding a NSTableView with an NSArrayController
- search in the table using NSPredicate to filter the array
Getting Started
Creating the Interface
Start by – this should come as no surprise to you – creating a new Cocoa project and open the MainMenu.xib in IB. Remove the Window from the Document Window and instead drag in a “Window and Drawer” (Library->Cocoa->Application->Windows).
Click the new window twice to open it and drag a Toolbar(Library->Cocoa->Application->Toolbars) onto it. In Library->Cocoa->Views & Cells->Inputs & Values which you can add to the toolbar(beforehand you need to click on the toolbar twice to open the configurator).
Remove everything unnecessary from the toolbar and add a “Image Toolbar Item”. When you select this item you can change its image in the attributes tab. Set it to “NSAddTemplate”. There you can also change the label.

Next, add a NSTableView to the window so that it fills the remaining space. Select the ScrollView surrounding the NSTableView and go to the Size tab where you should enable autosizing by clicking on the inner double arrows in the rectangle:

Now select the TableView and click on the Attributes tab here you can change the number of columns to 3. Resize the columns so that they look okay and rename them(click ‘em twice) to “First Name”, “Last Name”, “Phone Number”.
Click twice on the “Drawer Content View” in the Document Window so that it opens and add some labels and textfields so that it looks like this(altough I encourage you to make it look better):

Adding Outlets/Actions
Add an ArrayController(Library->Cocoa->Objects & Controllers->Controllers) and an Object(same place) to the Document Window. Change the “Class Name” of the Object to “WindowController”(Identity tab) in the same tab add the following things:

- 2 Actions:
- addContactAction
- updateFilterAction
- 6 Outlets:
- arrayContacts(NSArrayController)
- txtSearchField(NSSearchField)
- tblContacts(NSTableView)
- txtFirstName(NSTextField)
- txtLastName(NSTextField)
- txtPhoneNumber(NSTextField)
Finally go to File->Write Class Files.. while having the WindowController selected.
Click the toolbar twice, so that the configuration comes up and link the Add Contact button to the Drawer’s(in the Document Window) toggle action, so that it toggles the drawer’s state between in and out every time the button is clicked.
Now connect every Class Outlet and every Class Action that you created before(if you don’t know how to do that check out my other tutorials).
The Actual Implementation
Adding a property
Now we need to add a property for our contacts(If you want to learn more about properties see chapter 9 on this page). To add it open up WindowController.h and add a NSMutableArray contacts and a associated property “contacts”. Also specify the superclass of the WindowController which we choose to be NSWindowController:
@interface WindowController : NSWindowController {
IBOutlet NSArrayController *arrayContacts;
IBOutlet NSTableView *tblContacts;
IBOutlet NSTextField *txtFirstName;
IBOutlet NSTextField *txtLastName;
IBOutlet NSTextField *txtPhoneNumber;
IBOutlet NSSearchField *txtSearchField;
NSMutableArray* contacts;
}
- (IBAction)addContactAction:(id)sender;
- (IBAction)updateFilterAction:(id)sender;
@property (copy) NSArray* contacts;
@end
In the WindowController.m add a corresponding synthesize statement:
... @implementation WindowController @synthesize contacts; ...
Loading the Contacts from a file
Whenever you run the application we want to the contacts to be read from a .plist and added to the table, so let’s add a awakeFromNib to the WindowController.m which is called when the interface finished loading.
...
@implementation WindowController
@synthesize contacts;
NSString *filePath;
...
- (void)awakeFromNib {
//set up the path of the plist(which will reside in the Resource folder of our app
filePath = [[[NSBundle mainBundle] pathForResource:@"Contacts" ofType:@"plist"] retain];
//initialize the contacts array
contacts = [[NSMutableArray alloc] init];
//check if the file Contacts.plist exists
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
//if it does read its contents into an array and add the contacts to the arrayController wich will then feed the table
NSMutableArray *contactsFile = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
for (id contact in contactsFile) {
[arrayContacts addObject:contact];
}
}
}
...
Connecting the ArrayController to the TableView
Now we need to connect the ArrayController to the property we created, so open up Interface Builder and go to the Bindings tab while having the ArrayController selected. There go to “Content Array” and bind it to the Window Controller. As the “Model Key Path” choose “contacts”(the name of our property).
Keep the Bindings tab open and select the “First Name” column of the table, go to “Value” and bind it to the arrayController use “arrangedObjects” as the Controller Key and “firstName” as the Model Key Path.

Repeat this procedure for the other two columns and choose “lastName” and “phoneNumber” as Model Key Path.
Now whenever a new entry is added to the arrayController the table is filled with the informations you just binded to it.
The First Contact
You could run this to see if it works if you already had a Contacts.plist. If you want to try it, copy this to “build/Debug/Phonebook.app/Contents/Resources/Contacts.plist”:
< ?xml version="1.0" encoding="UTF-8"?> < !DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <array> <dict> <key>firstName</key> <string>John</string> <key>lastName</key> <string>Doe</string> <key>phoneNumber</key> <string>0123456789</string> </dict> </array> </plist>
Note that this is not necessary and only serves the purpose of having some sense of achievement.
Adding Contacts
To be able to add new contacts all we have to do is to fill the body of the addContactAction:
- (IBAction)addContactAction:(id)sender {
//create a new contact based on the input values
NSDictionary *dict =[NSDictionary dictionaryWithObjectsAndKeys:
[txtFirstName stringValue], @"firstName",
[txtLastName stringValue], @"lastName",
[txtPhoneNumber stringValue], @"phoneNumber",
nil];
//add it to the arrayController
[arrayContacts addObject:dict];
//save the changes to the plist
if ( ![[NSFileManager defaultManager] fileExistsAtPath:filePath] || [[NSFileManager defaultManager] isWritableFileAtPath:filePath]) {
[[arrayContacts arrangedObjects] writeToFile:filePath atomically:YES];
}
}
Try it out! Also try to restart the application to see if your changes have been saved.
Making the Seach work
We’ll be using predicates to filter the TableView. To learn more about the NSPredicate class see the Mac OS X Reference Library. It’s pretty similar to SQL if you’ve been using that before.
Here is how it works: First we create a “template” which contains the query but no actual values. We’ll do that in the awakeFromNib method:
...
@implementation WindowController
...
NSPredicate *predicateTemplate;
...
- (void)awakeFromNib {
//creating a predicate with the condition that either firstName, lastName or phoneNumber contains the searchString (case- and diacritic-insensitive)
predicateTemplate = [[NSPredicate predicateWithFormat:@"(firstName contains[cd] $searchString) or (lastName contains[cd] $searchString) or (phoneNumber contains[cd] $searchString)"] retain];
...
Whenever the value in the seach field changes the updateFilterAction is called, so let’s put our search logic there:
- (IBAction)updateFilterAction:(id)sender {
NSString *searchString = [txtSearchField stringValue];
NSPredicate *predicate;
//if the search field is empty do not search!
if ([searchString isEqualToString:@""]) {
predicate = nil;
} else {
//create a new dictionary with the search string
NSMutableDictionary *bindVariables = [[NSMutableDictionary alloc] init];
[bindVariables setObject:searchString forKey:@"searchString"];
//and create a predicate from the template by replacing the variable with its actual value
predicate = [predicateTemplate predicateWithSubstitutionVariables:bindVariables];
}
//apply the predicate to the array controller
[arrayContacts setFilterPredicate:predicate];
}
That’s all that needed to be done to make the search field work. Check it out!
The Source
The final Xcode project can be download from here.
Have fun!
[...] posted here: Phonebook/NSTableView Tutorial for Dummies(Xcode 3.1.3) « André's Blog Share and [...]
Phonebook/NSTableView Tutorial for Dummies(Xcode 3.1.3) « André's Blog | IPhoneMate
September 27, 2009 at 12:40 pm
Cracking tutorial mate, I found it really helpful.
Cheers!
ves
October 18, 2009 at 8:43 pm
Verry nice tut! thx..
Sampie
November 6, 2009 at 4:26 pm
[...] een plist schrijven Hallo, ik ben een simpel programma aan het schrijven naar aanleiding van deze tutorial. De gebruiker ziet een lijstje, kan daar een muziekbestand aan toevoegen en afspelen, en [...]
Array met dicts naar een plist schrijven - iPhone Forum - alles over de Apple iPhone, iPhone 3G en iPhone 3GS
November 18, 2009 at 10:49 am
Why the app does not save after restart? Thats´s not good. Also I can not delete a entry if it is wrong. That´s also not good.
Please answere me per mail: mauricio@web.de
Sorry for my bad english.
Mauricio Fritz
January 25, 2010 at 1:59 pm
Had anyone solved the problem with new rows that can’t be edited (saved) until the app is restarted or new row added?
Thank you.
mira
January 25, 2010 at 2:47 pm
Got it! Replace “NSDictionary” to “NSMutableDictionary”.
mira
January 26, 2010 at 12:38 am
Thanks!
it helpful!
sakrist
March 5, 2010 at 12:46 pm
*bookmarked* good and easy reference for obj-c beginner if you want to use the table view
Robin
October 7, 2010 at 3:29 pm
[...] http://andrehoffmann.wordpress.com/2009/09/03/phonebook-tutorial-for-dummiesxcode-3-1-3/ [...]
NSTableView Issues… | DeveloperQuestion.com
October 21, 2010 at 1:22 am
Phonebook/NSTableView Tutorial for Dummies(Xcode 3.1.3) André's Blog
seo copywriters
January 25, 2011 at 11:16 pm
Good tutorial! really helpfull
But I don’t understand where is the Contacts.plist file… I download ur code and try to do it myself :
yours does save changes but mine don’t….
can u help me? and tell me more about saving information (maybe other ways, make the program reads a .txt maybe….)
Carmi
February 7, 2011 at 12:55 am
Thanks i keep my job for this tutorial
Javier Beltrán
June 14, 2011 at 11:34 pm
Thannnnnkssssssssss!!!!
Enrico
August 1, 2011 at 12:49 pm