Fetching a user's contacts

In the introduction to this chapter, it was mentioned that we would use Contacts.framework to fetch a user's contacts list and display it in UITableView.

Before we get started, we need to be sure we have access to the user's address book. In iOS 10, privacy is a bit more restrictive than it was earlier. If you want to have access to a user's contacts, you need to specify this in your application's Info.plist file. If you fail to specify the correct keys for the data your application uses, it will crash without warning. So, before attempting to load your user's contacts, you should take care to add the correct key to Info.plist.

To add the key, open Info.plist from the list of files in the Project Navigator on the left and hover over Information Property List at the top of the file. A plus icon should appear, which will add an empty key with a search box when you click on it. If you start typing Privacy - contacts, Xcode will filter options until there is just one left, that is, the key for contact access. In the value column, fill in a short description about what you are going to use this access for. In our app, something like read contacts to display them in a list should be sufficient.

Note

Whenever you need access to photos, Bluetooth, camera, microphone, and more, make sure you check whether your app needs to specify this in its Info.plist. If you fail to specify a key that's required, your app will crash and will not make it past Apple's review process.

Now that you have configured your app to specify that it wants to be able to access contact data, let's get down to writing some code. Before reading the contacts, you'll need to make sure the user has given permission to access the contacts. You'll have to check this first, after which the code should either fetch contacts or it should ask the user for permission to access the contacts. Add the following code to ViewController.swift. After doing so, we'll go over what this code does and how it works:

class ViewController: UIViewController { 
 
    override func viewDidLoad() { 
        super.viewDidLoad() 
 
        let store = CNContactStore() 
 
        if CNContactStore.authorizationStatus(for: .contacts) == .notDetermined { 
            store.requestAccess(for: .contacts, completionHandler: {[weak self] 
              authorized, error in 
                if authorized { 
                    self?.retrieveContacts(fromStore: store) 
                } 
            }) 
        } else if CNContactStore.authorizationStatus(for: .contacts) == 
                    .authorized { 
            retrieveContacts(fromStore: store) 
        } 
    } 
 
    func retrieveContacts(fromStore store: CNContactStore) { 
        let keysToFetch = 
            [CNContactGivenNameKey as CNKeyDescriptor, 
             CNContactFamilyNameKey as CNKeyDescriptor, 
             CNContactImageDataKey as CNKeyDescriptor, 
             CNContactImageDataAvailableKey as CNKeyDescriptor] 
        let containerId = store.defaultContainerIdentifier() 
        let predicate = CNContact.predicateForContactsInContainer(withIdentifier: 
          containerId) 
        let contacts = try! store.unifiedContacts(matching: predicate, 
          keysToFetch: keysToFetch) 
 
        print(contacts) 
    } 
} 

In the viewDidLoad method, we will get an instance of CNContactStore. This is the object that will access the user's contacts database to fetch the results you're looking for. Before you can access the contacts, you need to make sure that the user has given your app permission to do so. First, check whether the current authorizationStatus is equal to .notDetermined. This means that we haven't asked permission yet and it's a great time to do so. When asking for permission, we pass a completionHandler. This handler is called a closure. It's basically a function without a name that gets called when the user has responded to the permission request. If your app is properly authorized after asking permission, the retrieveContacts method is called to actually perform the retrieval. If the app already has permission, we'll call retrieveContacts right away.

Completion handlers are found throughout the Foundation and UIKit frameworks. You typically pass them to methods that perform a task that could take a while and is performed parallel to the rest of your application, so the user interface can continue running without waiting for the result. A simplified implementation of such a function could look like the following:

func doSomething(completionHandler: Int -> Void) {    // perform some actions
    var resultingValue = theResultOfSomeAction() 
    completionHandler(resultingValue)

} 

You'll notice that actually calling completionHandler looks identical to calling an ordinary function or method. The idea of such a completion handler is that we can specify a block of code, a closure, that is supposed to be executed at a later time. For example, after performing a task that is potentially slow. You'll find plenty of other examples of callback handlers and closures throughout this book as it's a common pattern in programming.

The retrieveContacts method in ViewController.swift is responsible for actually fetching the contacts and is called with a parameter named store. It's set up like this so we don't have to create a new store instance since we already created one in viewDidLoad. When fetching a list of contacts, you use a predicate. We won't go into too much detail on predicates and how they work yet because that's covered more in depth in Chapter 9, Storing and Querying Data in Core Data . The main goal of the predicate is to establish a condition to filter the contacts database on.

In addition to a predicate, you also provide a list of keys your code wants to fetch. These keys represent properties that a contact object can have. They represent data, such as e-mail, phone numbers, names, and more. In this example, you only need to fetch a contact's given name, family name, and contact image. To make sure the contact image is available at all, there's a key request for that as well.

When everything is configured, a call is made to unifiedContacts(matching: keysToFetch:). This method call can throw an error, and since we're currently not interested in the error, try! is used to tell the compiler that we want to pretend the call can't fail and if it does, the application should crash.

When you're building your own app, you might want to wrap this call in do {} catch {} block to make sure that your app doesn't crash if errors occur. If you run your app now, you'll see that you're immediately asked for permission to access contacts. If you allow this, you will see a list of contacts printed in the console. Next, let's display some content information in the contacts table!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.253.198