Cocoa Touch Tutorial: Extract Address Book Address Values on iPhone OS

This is the first of what I hope to be several Cocoa Touch tutorials on this site.  I was doing some furious Googling last night trying to find out how to get a contact’s street address from the Address Book for an upcoming update to Take Me Home, and I realized that it’s complicated and there aren’t any good tutorials online.  So, after I figured it out, I commented it up so that hopefully, if you’re reading this, you’ll save some time that I didn’t.

Before you read this tutorial, you should go through Apple’s excellent Address Book Programming Guide for iPhone OS.  This tutorial will rely on the QuickStart application you write in the guide, so do that first.
The first thing we need to do is add an address field to the QuickStart application.  Use Interface Builder to add a new UILabel underneath the two you already have.  You may want to stretch it to fill the entire width of the screen, like so:

Add a new UILabel underneath the exisiting two.
Add a new UILabel underneath the exisiting two.

Now, add the information about this label to QuickStartViewController.h:

//
//  QuickStartViewController.h
//  QuickStart
//

#import <UIKit/UIKit.h>
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>

@interface QuickStartViewController : UIViewController <ABPeoplePickerNavigationControllerDelegate> {
    IBOutlet UILabel *firstName;
    IBOutlet UILabel *lastName;
    IBOutlet UILabel *addressLabel;
}

@property (nonatomic, retain) UILabel *firstName;
@property (nonatomic, retain) UILabel *lastName;
@property (nonatomic, retain) UILabel *addressLabel;

- (IBAction)showPicker:(id)sender;

@end

Be sure to go back into Interface Builder and connect File’s Owner in QuickStartViewController.xib to addressLabel.

Now, we have to change the method that gets called when you click on a person in the ABPeoplePicker.  As it is at the end of the QuickStart tutorial, once you select a person the picker is dismissed.  So, we do the following in QuickStartViewController.m:

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
      shouldContinueAfterSelectingPerson:(ABRecordRef)person {
    NSString *name = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
    self.firstName.text = name;
    [name release];

    name = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
    self.lastName.text = name;
    [name release];

    [self dissmissModalViewControllerAnimated:YES];

    return YES;
}

Note that you have to delete the line that dismisses the modal view controller; if you don’t, the people picker is dismissed before you have a chance to get the address. When you delete it, the people picker will continue when you select a person. Next up, we have to write the method for when someone selects an address on the next screen. Here’s the method:

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
      shouldContinueAfterSelectingPerson:(ABRecordRef)person
                                property:(ABPropertyID)property
                              identifier:(ABMultiValueIdentifier)identifier {
    // Only inspect the value if it's an address.
    if (property == kABPersonAddressProperty) {
        /*
         * Set up an ABMultiValue to hold the address values; copy from address
         * book record.
         */
        ABMultiValueRef multi = ABRecordCopyValue(person, property);

        // Set up an NSArray and copy the values in.
        NSArray *theArray = [(id)ABMultiValueCopyArrayOfAllValues(multi) autorelease];

        // Figure out which values we want and store the index.
        const NSUInteger theIndex = ABMultiValueGetIndexForIdentifier(multi, identifier);

        // Set up an NSDictionary to hold the contents of the array.
        NSDictionary *theDict = [theArray objectAtIndex:theIndex];

        // Set up NSStrings to hold keys and values.  First, how many are there?
        const NSUInteger theCount = [theDict count];
        NSString *keys[theCount];
        NSString *values[theCount];

        // Get the keys and values from the CFDictionary.  Note that because
        // we're using the "GetKeysAndValues" function, you don't need to
        // release keys or values.  It's the "Get Rule" and only applies to
        // CoreFoundation objects.
        [theDict getObjects:values andKeys:keys];

        // Set the address label's text.
        NSString *address;
        address = [NSString stringWithFormat:@"%@, %@, %@, %@ %@",
                   [theDict objectForKey:(NSString *)kABPersonAddressStreetKey],
                   [theDict objectForKey:(NSString *)kABPersonAddressCityKey],
                   [theDict objectForKey:(NSString *)kABPersonAddressStateKey],
                   [theDict objectForKey:(NSString *)kABPersonAddressZIPKey],
                   [theDict objectForKey:(NSString *)kABPersonAddressCountryKey]];

        self.addressLabel.text = address;

        // Return to the main view controller.
        [ self dismissModalViewControllerAnimated:YES ];
        return NO;
    }

    // If they didn't pick an address, return YES here to keep going.
    return YES;
}

Let’s go through that in more detail.  The method gives us the following information: an ABRecordRef of the person we’ve selected, an ABPropertyID of the property slected (in this case, we ensure that it’s the address) and an ABMultiValueIdentifier of which address we’ve selected.  It is important to note that the ABPropertyID is equal to kABPersonAddressProperty when you select any address; that is, there is only one address property. This one address property holds the values in an ABMultiValue, each at a specific index.  Here are the steps we take in the code:

  1. The first thing we do is define our ABMultiValue, multi, and copy the contents of the selected value into it.
  2. Then we define an NSArray, theArray, into which to copy the multiple values.  But which one do we want?
  3. Each address has an identifier, which the method gives to us as identifier, but we reference them by index when getting them out of the array.  So, we need to create an index (which we’ll store as an unsigned integer), theIndex, and set it to the return value of the ABMultiValueGetIndexForIdentifier function.  Now that we have the index, we know which value of the array to store .  They’re stored as type CFDictionary, which have key-value pairs for us to use, so we define an NSDictionary, theDict to put them into.
  4. First, we need to know how many key-value pairs there are, so we use the count method and store the return value in an unsigned integer, theCount.  Be sure that this variable doesn&rquo;t change—you don’t want to assume that there are more members in the array than there actually are, as that can lead to nasty memory problems. For that reason I’ve defined it as a constant.
  5. Now, we define two NSString arrays, keys[theCount] and values[theCount], and then we’re ready for action.
  6. Next we use the NSDictionary getObjects: andKeys: function to copy the keys and values. The function copies the data, and we can construct our street address.  For the purpose of this example, I’m going to make the address a single line, but you do with it what you want.
  7. Finally, we create a final NSString to put the formatted address into, pull the values out of the dictionary into the appropriate place, and we’re all done!

Update 2010-07-27: Removed [ theDict release ];, some bad memory management.

New Safari 3.2 Feature: Secure Website Identification

Here’s a quick tip that slipped through the blogosphere (at least none of the Mac blogs I subscribe to featured it): in Safari 3.2, released last week, Apple’s added a feature from Firefox 3’s “awesome bar”: when you’re on a secure website, such as a bank’s, that has identification information, it’s displayed in green (though in Safari it’s at the top-right of the title bar).  A screenshot:

 

Safari 3.2 adds secure website information to the title bar.
Safari 3.2 adds secure website information to the title bar.

Along with a phishing filter, it looks like Safari is stepping up to the plate as a secure browser.

Use DVD Player in Fullscreen Mode on an External Monitor

By default, DVD player will exit fullscreen mode when it’s not the active application.  This is a problem if you want to watch a movie on an external monitor while working on a primary monitor.  To get around it, go to Preferences in DVD Player (DVD Player -> Preferences… or command + ,), switch to the “Full Screen” tab, and ensure that “Remain in full screen when DVD Player is inactive” is checked.  This should achieve the desired results.

Source: MacRumors.com Forums

Use Your MacBook Pro with an External Monitor Without Sleeping

So, in a similar vein as to what pushed me to write my Applescript to resize windows, I’ve been looking at what to do about going from using the LCD on the MacBook pro to an external monitor.  Now, everyone knows that in order to use an external display, you have to connect the display adapter while the notebook is closed, plug in an external keyboard (and your power supply), and press a button, and boom, you’ve got external display action at your monitor’s native resolution.  But what if you don’t want to wait the ten seconds or so it takes to go from awake to asleep?  Messing with it, I was happy to note that the following procedure seems to work:

  1. Plug in the external display, your keyboard/mouse, your power supply, etc—with your notebook open.  The external display will mirror your notebook’s LCD, at its resolution (if supported by the display.  If it isn’t, you’ll get the highest common denominator, I think).
  2. Close your notebook cover so the display turns off.
  3. Immediately open the notebook cover, then close it just as soon, then push a button on your keyboard.
  4. Presto! Your MacBook Pro should see the display and change the resolution how you want it.

I’ve only tested this on my machine, so let me know in the comments if it works/doesn’t work or if you have a better way.