Address Book Tutorial in Swift and iOS

In this Address Book Tutorial for iOS, learn how to add, edit and display contacts in a fun app about pets. By Niv Yahel.

Leave a rating/review
Save for later
Share

Meet our Furry Little Friends!

Meet our Furry Little Friends!

Update note: This tutorial was updated to Swift and iOS 8 by Niv Yahel. The original Objective-C tutorial was written by Tutorial Team member Evan Dekhayser.

Note: The contacts framework has changed significantly in iOS 9; this tutorial does not cover those changes. However this tutorial is still valuable if you wish to support iOS 8 and prior.

The Address Book framework allows you to read or modify the user’s contacts from your apps; these are the same contacts that show up in the Contacts app.

When using frameworks with Swift, not all of the ones you’ll encounter are object-oriented. Address Book has a C-based API, which doesn’t use objects; instead it uses opaque pointer-like types. In this Address Book tutorial, you’ll become familiar with a few of these:

  • ABAddressBookRef, the top-level collection of all the user’s contacts.
  • ABRecordRef, an individual contact record with properties such as name and company.
  • ABMultiValueRef and ABMutableMultiValueRef, which let you set properties of the ABRecordRef that may have multiple entries, such as phone number or email.

This Address Book tutorial assumes you are familiar with the basics of iOS development, and are comfortable with the basics of Swift. Let’s dive in and get some addresses!

Note: Apple has announced a new Contacts framework that will provide an object-oriented replacement to the Address Book API. That framework will be available when iOS 9 is released, which means you’ll need to continue using Address Book for your apps in the meantime.

Getting Started

To begin this tutorial, download the starter project that has the user interface pre-made, so you can stay focused on the Address Book part of the tutorial.

Build and run, and get ready to meet our furry little friends: Cheesy, Freckles, Maxi, and Shippo!

PetBook-App

In this app, the user will press the image of one of the pets, and the pet’s contact information will be added to their address book (I’m surprised that even pets have iPhones!). Once added, you will be able to choose to add them to an “Animals” group to better contact them all in the future. Lastly, when tapping on an already added pet, you will be able to view and modify their information in a ABPersonViewController. Using the power of the Address Book API, you can finally contact your favorite furry friends.

Back in Xcode, open PetBookViewController.swift. The starter app has four UIButtons that all call tappedAddPetToContacts(_:) when pressed. If you look closely in the Utilities/Attributes Inspector in Main.storyboard, notice that each button has a different number in its tag. This will help you distinguish which button is the one that called tappedAddPetToContacts(_:).

To get started with the Address Book API, add the following line to the top of PetBookViewController.swift

import AddressBook

This will allow you to access the functions and types in the framework.

Asking for Permission

In 2012, there was a controversy regarding certain apps sending a copy of the user’s Address Book to its servers. Because of the public’s response to this security breach, Apple immediately implemented security features to prevent this from happening without the user’s knowledge.

So now, whenever you want to use the Address Book, you first ask the user for permission.

In PetBookViewController.swift, add the following code inside tappedAddPetToContacts(_:)

let authorizationStatus = ABAddressBookGetAuthorizationStatus()

switch authorizationStatus {
  case .Denied, .Restricted:
    //1
    println("Denied")
  case .Authorized:
    //2
    println("Authorized")
  case .NotDetermined:
    //3
    println("Not Determined")
}

You can already see the lack of classes and objects here – to get the current authorization status, you call ABAddressBookGetAuthorizationStatus(). There are several different code paths depending on the return value here:

  1. This checks to see if the user has either denied your app access to the Address Book in the past, or if it is restricted because of parental controls. In this case, all you can do is inform the user that you can’t add the contact because the app does not have permission.
  2. This checks to see if the user has already given your app permission to use their Address Book. In this case, you are free to modify the Address Book however you want.
  3. This checks to see if the user hasn’t decided yet whether not to give permission to your app.

Build and run, and tap the cutest pet. You should see something like the following in the console:

PetBook[832:70b] Not Determined

You now know how to check the authorization status, but when you want something, you ask. Same thing here!

First, you need a reference to the user’s address book. Add the following property to the class:

let addressBookRef: ABAddressBook = ABAddressBookCreateWithOptions(nil, nil).takeRetainedValue()

ABAddressBookCreateWithOptions() will return a reference to the current address book. You don’t need to pass in any options or get an error handle back, so both parameters are nil.

Note: Some C APIs (including Address Book) haven’t been annotated by Apple, which prevents the compiler from automatically managing the memory of their objects. That means functions will often return Unmanaged objects, which as the name suggests, are unmanaged in terms of memory management.

Memory management is beyond the scope of this tutorial but what you’ll see throughout this project is calling takeRetainedValue() on returned references. This ensures the objects are retained and aren’t released from memory while you still might need to use them!

Now that you have a reference to the address book, add the following method to the class:

func promptForAddressBookRequestAccess(petButton: UIButton) {
  var err: Unmanaged<CFError>? = nil
  
  ABAddressBookRequestAccessWithCompletion(addressBookRef) {
    (granted: Bool, error: CFError!) in
    dispatch_async(dispatch_get_main_queue()) {
      if !granted {
        println("Just denied")
      } else {
        println("Just authorized")
      }
    }
  }
}

ABAddressBookRequestAccessWithCompletion() will ask the user for access to the address book, and takes two arguments – the first is a reference to an address book, which you already have a property for. The second is a closure expression that will run once the user either allows or denies access.

You can see how granted is a Boolean passed into the closure to let you know which choice the user made. For now, you’ll just log out the results.

If you look at the documentation for ABAddressBookRequestAccessWithCompletion, the completion is called on an arbitrary queue. In other words, it may be called on a thread besides the main thread. Later on, you’ll be doing things such as showing alert dialogs so the closure expression starts with a dispatch to the main queue.

Head back to tappedAddPetToContacts(_:) and add the following line to the end of the .NotDetermined case:

promptForAddressBookRequestAccess(petButton)

That means you’ll prompt the user for permission the very first time they tap on a pet.

Build and run the app, and this time tap on the weirdest looking pet. A popup will appear to request access to the Address Book:

PetBook-ContactPermissions

Depending on your choice, you’ll see either “Just authorized” or “Just denied” in the console. Now, tap an image again, and you’ll see the result is related to your action before: if you gave permission, it will say “Authorized”, or else it will say “Denied”.

Note: To test both cases, you can stop the app and then use the iOS Simulator/Reset Content and Settings option in the simulator app. This will clear out all the apps and settings in the simulator, allowing you to “re-install” the app and get a fresh start.

One issue with ABAddressBookRequestAccessWithCompletion() is that once the user gives the app permission, sometimes there is a 5-10 second delay until the completion is called. This can make it seem like the app’s frozen when it’s adding the contact. In most cases, this is not too much of an issue.

What happens if the user doesn’t allow access? Is your app stuck forever? Back in Xcode, add the following method to PetBookViewController:

func openSettings() {
  let url = NSURL(string: UIApplicationOpenSettingsURLString)
  UIApplication.sharedApplication().openURL(url!)
}

New in iOS 8 is the ability to send the user to the Settings app, right to the page with your app’s permissions. This method will do just that, using your app’s specific settings URL.

Next, you’ll need a method to show an alert and prompt the user to re-think their choice if they didn’t allow address book access. Add the following method to the class:

func displayCantAddContactAlert() {
  let cantAddContactAlert = UIAlertController(title: "Cannot Add Contact",
    message: "You must give the app permission to add the contact first.",
    preferredStyle: .Alert)
  cantAddContactAlert.addAction(UIAlertAction(title: "Change Settings",
    style: .Default,
    handler: { action in
      self.openSettings()
  }))
  cantAddContactAlert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
  presentViewController(cantAddContactAlert, animated: true, completion: nil)
}

This method will tell the user that they can’t add the contact because the app doesn’t have the needed permissions. This will present a UIAlertController, which allows them either to continue or be directed to the settings page of the app by invoking openSettings().

Find promptForAddressBookRequestAccess(_:) and add the following line to the if block where you’re logging out “Just denied”:

self.displayCantAddContactAlert()

Next, find tappedAddPetToContacts(_:) and add the following line to the end of the .Denied, .Restricted case:

displayCantAddContactAlert()

Adding these method calls will show the alert if the user doesn’t allow access initially, and also when the user taps on a pet button after still not allowing access.

Use iOS Simulator/Reset Content and Settings to reset your simulator. Then build and run, tap the cutest pet, and deny permission. You should see the alert dialog appear, where you should tap “Change Settings” to be whisked away to the Settings app to correct your rejection of the cutest pet.

PetBook-Settings

When you change the permission in the Settings app, your app will crash. Not to worry – this is just a simulator issue and you can just return to Xcode and run the app again.

Niv Yahel

Contributors

Niv Yahel

Author

Over 300 content creators. Join our team.