Extracting the contact-fetching code

Start off by creating a new Swift file called ContactFetchHelper.swift. After creating the file, add it to a new folder called Helpers. First, you will extract all the contact-fetching code to the ContactFetchHelper struct. Then, you'll refactor ViewController.swift by replacing all contact-fetching code, so it uses the new ContactFetchHelper. The following code shows the implementation for ContactFetchHelper:

import Contacts

struct ContactFetchHelper {
  // 1
  typealias ContactFetchCallback = ([Contact]) -> Void

  let store = CNContactStore()

  // 2
  func fetch(withCallback callback: @escaping
    ContactFetchCallback) {
    if CNContactStore.authorizationStatus(for: .contacts) == .notDetermined {
      store.requestAccess(for: .contacts, completionHandler:
        {authorized, error in
          if authorized {
            self.retrieve(withCallback: callback)
          }
      })
    } else if CNContactStore.authorizationStatus(for: .contacts) == .authorized {
      retrieve(withCallback: callback)
    }
  }

  // 3
  private func retrieve(withCallback callback: ContactFetchCallback) {
    let containerId = store.defaultContainerIdentifier()

    let keysToFetch =
      [CNContactGivenNameKey as CNKeyDescriptor,
       CNContactFamilyNameKey as CNKeyDescriptor,
       CNContactImageDataKey as CNKeyDescriptor,
       CNContactImageDataAvailableKey as CNKeyDescriptor,
       CNContactEmailAddressesKey as CNKeyDescriptor,
       CNContactPhoneNumbersKey as CNKeyDescriptor,
       CNContactPostalAddressesKey as CNKeyDescriptor]

    let predicate = CNContact.predicateForContactsInContainer(withIdentifier: containerId)

    guard let retrievedContacts = try? store.unifiedContacts(matching:
      predicate, keysToFetch: keysToFetch) else {
        // call back with an empty array if we fail to retrieve contacts
        callback([])
        return
    }

    let contacts: [Contact] = retrievedContacts.map { contact in
      return Contact(contact: contact)
    }

    callback(contacts)
  }
}

This simple struct now contains all the required logic to fetch contacts. Let's go through some of the most interesting parts of code in the ContactsFetchHelper struct.

The first highlight points to an alias, named ContactFetchCallback, for a closure that receives an array of Contact instances and returns nothing. This is the closure that is passed to the fetch(withCallback:) method, and it's called after the fetch is performed.

The fetch(withCallback :) method is the method that should be called whenever you want to fetch contacts. The only argument it takes is a closure that will be called when the contacts are fetched. The fetch method performs the same authorization check that was in the view controller's viewDidLoad method before.

The third point of interest is a private method, called retrieve(withCallback:), that retrieves the contacts. The fetch(withCallback:) method calls this method and passes it the callback that it received. Once retrieve(withCallback:) has retrieved the contacts, it calls the callback with the array of fetched contacts.

The ContactFetchCallback that fetch(withCallback:) receives is marked as @escaping. This keyword indicates that the supplied closure will escape the scope it was passed to. In this example, callback is passed to the scope of fetch(withCallback:), which in turn passes this closure to the scope of retrieve(withCallback:).

In ViewController.swift, all you need to do is use the following code to retrieve contacts:

let contactFetcher = ContactFetchHelper()   
contactFetcher.fetch { [weak self] contacts in   
    self?.contacts = contacts   
    self?.collectionView.reloadData()   
} 

You can delete the retrieveContacts method entirely, and the preceding snippet replaces the code that checked for permissions in viewDidLoad. Also, because the Contacts framework is not used directly anymore, you can remove its import at the top of the file. You have now successfully extracted the contact-fetching code into a struct, and you're using a typealias to make your code more readable. This is already a big win for maintainability and reusability. Now, let's extract the bounce animation code as well.

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

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