Contacts Demo in Swift 2 – Showing Contacts Picker, Load Contacts in Custom TableView, Add New Contact, Update Contact, Delete Contact, Search Contact

By | February 21, 2016

In Today’s post I will show you How to do below things in Swift 2…

    1. Show Contact Picker using System In-Built UI & Pick a Contact
    2. Load all Contacts in a TableView
    3. Search a Contact
    4. Add a New Contact
    5. Update a Contact
    6. Delete a Contact


Before using the contacts we need to get permission from the User. For that we need to call some functions. I will write those functions in the AppDelegate method.


My AppDelegate will look like this.

import UIKit
import ContactsUI
import AddressBook
 
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
 
var window: UIWindow?
let contactStore = CNContactStore()
 
func showAlertMessage(message: String) {
     
    let alertController = UIAlertController(title: "Contacts Demo Swift", message: message, preferredStyle: UIAlertControllerStyle.Alert)
     
    let dismissAction = UIAlertAction(title: "OKAY", style: UIAlertActionStyle.Default) { (action) -> Void in
    }
     
    alertController.addAction(dismissAction)
     
    let pushedViewControllers = (self.window?.rootViewController as! UINavigationController).viewControllers
    let presentedViewController = pushedViewControllers[pushedViewControllers.count - 1]
     
    //Show the Alert
    presentedViewController.presentViewController(alertController, animated: true, completion: nil)
     
}
 
func requestForAccess(completionHandler: (accessGranted: Bool) -> Void) {
    let authorizationStatus = CNContactStore.authorizationStatusForEntityType(CNEntityType.Contacts)
     
    switch authorizationStatus {
    case .Authorized:
        completionHandler(accessGranted: true)
         
    case .Denied, .NotDetermined:
        self.contactStore.requestAccessForEntityType(CNEntityType.Contacts, completionHandler: { (access, accessError) -> Void in
            if access {
                completionHandler(accessGranted: access)
            }
            else {
                if authorizationStatus == CNAuthorizationStatus.Denied {
                    dispatch_async(dispatch_get_main_queue(), { () -> Void in
                        let message = "\(accessError!.localizedDescription)\n\nPlease allow the app to access your contacts through the Settings."
                        self.showAlertMessage(message)
                    })
                }
            }
        })
         
    default:
        completionHandler(accessGranted: false)
    }
}
}

1. Show Contact Picker using System In-Built UI & Pick a Contact



How to Do

You need to use an instance of the CNContactPickerViewController class inside the ContactsUI framework. It has two delegate methods.

func contactPickerDidCancel(picker: CNContactPickerViewController)
Gets called when the user cancels his request to pick a contact.

contactPicker(picker: CNContactPickerViewController, didSelectContact contact: CNContact)
Gets called when the user picks a contact from the Contacts list.

CNContactPickerViewController cannot be pushed to the stack, they need to be presented modally.
So For showing the CNContactPickerViewController you need to do like this.

self.presentViewController(peoplePicker, animated: true, completion: nil)

I will show up the Contacts List using the Inbuild System UI.

Now we will implement “didSelectContact” delegate…

If the person has multiple contact numbers, then it will show an alert to select one..

func contactPicker(picker: CNContactPickerViewController, didSelectContact contact: CNContact) {
    self.statusLabel.hidden = true
    //Dismiss the picker VC
    picker.dismissViewControllerAnimated(true, completion: nil)
    //See if the contact has multiple phone numbers
    if contact.phoneNumbers.count > 1 {
         
        self.showContactView.hidden = false
         
        //If so we need the user to select which phone number we want them to use
        let multiplePhoneNumbersAlert = UIAlertController(title: "Which one?", message: "This contact has multiple phone numbers, which one did you want use?", preferredStyle: UIAlertControllerStyle.Alert)
        //Loop through all the phone numbers that we got back
        for number in contact.phoneNumbers {
            //Each object in the phone numbers array has a value property that is a CNPhoneNumber object, Make sure we can get that
            if let actualNumber = number.value as? CNPhoneNumber {
                //Get the label for the phone number
                var phoneNumberLabel = number.label
                //Strip off all the extra crap that comes through in that label
                phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("_", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("$", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("!", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("<", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString(">", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                //Create a title for the action for the UIAlertVC that we display to the user to pick phone numbers
                let actionTitle = phoneNumberLabel + " - " + actualNumber.stringValue
                //Create the alert action
                let numberAction = UIAlertAction(title: actionTitle, style: UIAlertActionStyle.Default, handler: { (theAction) -> Void in
                    //Create an empty string for the contacts name
                    var nameToSave = ""
                    //See if we can get A frist name
                    if contact.givenName == "" {
                        //If Not check for a last name
                        if contact.familyName == "" {
                            //If no last name set name to Unknown Name
                            nameToSave = self.UNKNOWN_NAME
                        }else{
                            nameToSave = contact.familyName
                        }
                    }else{
                        nameToSave = contact.givenName
                    }
                     
                    // See if we can get image data
                    if let imageData = contact.imageData {
                        //If so create the image
                        let userImage = UIImage(data: imageData)
                        self.imgContact.image = userImage;
                    }
                    //Do what you need to do with your new contact information here!
                    //Get the string value of the phone number like this:
                    actualNumber.stringValue
                     
                    self.lblName.text = nameToSave;
                    self.lblNumber.text = actualNumber.stringValue
                })
                //Add the action to the AlertController
                multiplePhoneNumbersAlert.addAction(numberAction)
            }
        }
        //Add a cancel action
        let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: { (theAction) -> Void in
            //Cancel action completion
            print("User Cancelled")
        })
        //Add the cancel action
        multiplePhoneNumbersAlert.addAction(cancelAction)
        //Present the ALert controller
        self.presentViewController(multiplePhoneNumbersAlert, animated: true, completion: nil)
    }else{
        //Make sure we have at least one phone number
        if contact.phoneNumbers.count > 0 {
             
            self.showContactView.hidden = false
             
            //If so get the CNPhoneNumber object from the first item in the array of phone numbers
            if let actualNumber = contact.phoneNumbers.first?.value as? CNPhoneNumber {
                //Get the label of the phone number
                var phoneNumberLabel = contact.phoneNumbers.first!.label
                //Strip out the stuff you don't need
                phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("_", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("$", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("!", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString("<", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                phoneNumberLabel = phoneNumberLabel.stringByReplacingOccurrencesOfString(">", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
                 
                //Create an empty string for the contacts name
                var nameToSave = ""
                //See if we can get A frist name
                if contact.givenName == "" {
                    //If Not check for a last name
                    if contact.familyName == "" {
                        //If no last name set name to Unknown Name
                        nameToSave = UNKNOWN_NAME
                    }else{
                        nameToSave = contact.familyName
                    }
                }else{
                    nameToSave = contact.givenName
                }
                 
                self.lblName.text = nameToSave
                 
                // See if we can get image data
                if let imageData = contact.imageData {
                    //If so create the image
                    let userImage = UIImage(data: imageData)
                    self.imgContact.image = userImage;
                }
                //Do what you need to do with your new contact information here!
                //Get the string value of the phone number like this:
                print(actualNumber.stringValue)
                self.lblNumber.text = actualNumber.stringValue
            }
        }else{
             
            self.statusLabel.hidden = false;
            self.statusLabel.text = NO_PHONE_NUMBERS
             
        }
    }
}

contactPickerDidCancel Delegate..

func contactPickerDidCancel(picker: CNContactPickerViewController) {
    picker.dismissViewControllerAnimated(true, completion: nil)
}

2. Load all contacts from the System Contacts.


I am assuming that you will have a Tableview with Custom Cell for displaying a Contact Name and Thumbnail image of the contact.

The below function will fetch all the contacts.

func fetchContacts()
{     
    dataArray = NSMutableArray()
     
    let toFetch = [CNContactGivenNameKey, CNContactImageDataKey, CNContactFamilyNameKey, CNContactImageDataAvailableKey]
    let request = CNContactFetchRequest(keysToFetch: toFetch)
     
    do{
        try appDelegate.contactStore.enumerateContactsWithFetchRequest(request) {
            contact, stop in
            print(contact.givenName)
            print(contact.familyName)
            print(contact.identifier)
             
            var userImage : UIImage;
            // See if we can get image data
            if let imageData = contact.imageData {
                //If so create the image
                userImage = UIImage(data: imageData)!
            }else{
                userImage = UIImage(named: "no_contact_image")!
            }
             
            let data = Data(name: contact.givenName, image: userImage)
            self.dataArray?.addObject(data)
             
        }
    } catch let err{
        print(err)
        self.errorStatus()
    }
     
    self.tableView.reloadData()
     
}

3. Search a Contact.

There are actually three different methods for searching contacts.

1. unifiedContactsMatchingPredicate(_:keysToFetch:) method of CNContactStore
This allows you to fetch all contacts that match a certain predicate.

2. enumerateContactsWithFetchRequest(_:usingBlock:) method of CNContactStore
This allows you to enumerate through all contacts that match a fetch request.
The fetch request can have a predicate if you want it to.
Otherwise, you can use this method with a request object that does not have a predicate, in order to fetch all contacts.

3. unifiedContactWithIdentifier(_:keysToFetch:) method of CNContactStore
This fetches only a single contact with a given identifier, if it can find one.
Use this method to fetch properties for a partially fetched contact.

Code

func findInContacts()
{
     
    dataArray = NSMutableArray()
     
    let searchKey = "coderzheaven"
    var toFetch = [CNContactGivenNameKey, CNContactImageDataKey, CNContactFamilyNameKey, CNContactImageDataAvailableKey]
    let predicate = CNContact.predicateForContactsMatchingName(searchKey)
     
    do{
         
     
        let contacts = try appDelegate.contactStore.unifiedContactsMatchingPredicate(predicate,
            keysToFetch: toFetch)
         
        for contact in contacts{
             
            /*
                guard contact.imageDataAvailable else{
                    continue
                }
            */
             
            if contact.isKeyAvailable(CNContactImageDataKey){
                
                var userImage : UIImage;
                // See if we can get image data
                if let imageData = contact.imageData {
                    //If so create the image
                    userImage = UIImage(data: imageData)!
                }else{
                    userImage = UIImage(named: "no_contact_image")!
                }
                 
                let data = Data(name: contact.givenName, image: userImage)
                self.dataArray?.addObject(data)
                 
            } else {
                 
                toFetch += [CNContactImageDataKey, CNContactGivenNameKey]
                do{
                    let contact = try appDelegate.contactStore.unifiedContactWithIdentifier(
                        contact.identifier, keysToFetch: toFetch)
                    print(contact.givenName)
                    print(contact.identifier)
                     
                } catch let err{
                    print(err)
                }
                 
            }
             
        }
         
         self.tableView.reloadData()
     
    } catch let err{
        print(err)
    }
 
}

4. Add a New Contact..

We have to do the following things to add a contact

  • Request access from the user for accessing Contacts.
  • Create an instance of the CNMutableContact class.
  • Set the properties we want to fetch from the Contacts Database like given name, email address etc.
  • Instantiate CNSaveRequest, call the addContact(_:toContainerWithIdentifier:) method on it, and pass your contact to it. Set the container ID to nil.
    Once you have the request, execute it on your store instance using executeSaveRequest(_:).

The below fuction will add a contact to the Contacts Database.

func createContact()
{
    let fooBar = CNMutableContact()
    fooBar.givenName = "Coderz"
    fooBar.middleName = "Heaven"
    fooBar.familyName = "Coderz"
    fooBar.nickname = "Coderz"
     
    //profile photo
    if let img = UIImage(named: "no_contact_image"),
        let data = UIImagePNGRepresentation(img){
            fooBar.imageData = data
    }
     
    //set the phone numbers
    let homePhone = CNLabeledValue(label: CNLabelHome,
        value: CNPhoneNumber(stringValue: "1234567"))
    let workPhone = CNLabeledValue(label: CNLabelWork,
        value: CNPhoneNumber(stringValue: "9876543"))
    fooBar.phoneNumbers = [homePhone, workPhone]
     
    //set the email addresses
    let homeEmail = CNLabeledValue(label: CNLabelHome,
        value: "coderzheaven@gmail.com")
    let workEmail = CNLabeledValue(label: CNLabelWork,
        value: "coderzheaven@gmail.com")
    fooBar.emailAddresses = [homeEmail, workEmail]
     
    //job info
    fooBar.jobTitle = "Software Developer"
    fooBar.organizationName = "CoderzHeaven"
    fooBar.departmentName = "IT"
     
    //social media
    let facebookProfile = CNLabeledValue(label: "FaceBook", value:
        CNSocialProfile(urlString: nil, username: "CoderzHeaven",
            userIdentifier: nil, service: CNSocialProfileServiceFacebook))
    let twitterProfile = CNLabeledValue(label: "Twitter", value:
        CNSocialProfile(urlString: nil, username: "coderzheaven",
            userIdentifier: nil, service: CNSocialProfileServiceTwitter))
    fooBar.socialProfiles = [facebookProfile, twitterProfile]
     
    //instant messaging
    let skypeAddress = CNLabeledValue(label: "Skype", value:
        CNInstantMessageAddress(username: "coderzheaven",
            service: CNInstantMessageServiceSkype))
    fooBar.instantMessageAddresses = [skypeAddress]
     
    //some additional notes
    fooBar.note = "Heaven of all working codes."
     
    //birthday
    let birthday = NSDateComponents()
    birthday.year = 1987
    birthday.month = 9
    birthday.day = 27
    fooBar.birthday = birthday
     
    //finally save
    let request = CNSaveRequest()
    request.addContact(fooBar, toContainerWithIdentifier: nil)
    do{
        try appDelegate.contactStore.executeSaveRequest(request)
        print("Successfully added the contact")
        self.statusLabel.text = ("Successfully added the contact \n\(fooBar.givenName)")
    } catch let err{
        print("Failed to save the contact. \(err)")
        self.statusLabel.text = ("Failed to add the contact")
    }
}

5. Update a Contact.



How to do?

  • Call the mutableCopy() method of your CNContact class will give you an instance of the CNMutableContact
  • Change properties as you would with a contact of type CNContact.
  • Instantiate CNSaveRequest, call the updateContact(_:) method on it, and pass your mutable contact to that method.

Code

func updateContact()
{
    let predicate = CNContact.predicateForContactsMatchingName("coderzheaven")
    let toFetch = [CNContactGivenNameKey]
     
    do{
         
        let contacts = try appDelegate.contactStore.unifiedContactsMatchingPredicate(predicate,
            keysToFetch: toFetch)
         
        guard contacts.count > 0 else{
            print("No contacts found")
            return
        }
         
        //only do this to the first contact matching our criteria
        guard let contact = contacts.first else{
            return
        }
         
        let newName = "CoderzHeaven New"
         
        let newContact = contact.mutableCopy() as! CNMutableContact
         
        //set new name
        newContact.givenName = newName
         
        let req = CNSaveRequest()
        req.updateContact(newContact)
         
        /** To add new email address
         
        let emailAddress = CNLabeledValue(label: CNLabelWork,
            value: "newemail@email.com")
         
        newContact.emailAddresses.append(emailAddress)
 
        **/
         
        try appDelegate.contactStore.executeSaveRequest(req)
         
        print("Successfully edited")
         
        self.statusLabel.hidden = false
        self.statusLabel.text = "Successfully Updated coderzheaven"
         
}
         
catch let err{
    print(err)
    self.errorStatus()
}
 
 
}

6. Delete a Contact

How to ?

  • Find the Contact to delete using the methods mentioned in the Search Contact Section
  • Instantiate an object of type CNSaveRequest.
  • Issue the deleteContact(_:) function on the request and pass your mutable contact to it.
  • Execute your request using the executeSaveRequest(_:) method of your contact store.

Code

func deleteContact()
{
    let predicate = CNContact.predicateForContactsMatchingName("coderzheaven")
    let toFetch = [CNContactEmailAddressesKey]
     
    do{
         
        let contacts = try appDelegate.contactStore.unifiedContactsMatchingPredicate(predicate,
            keysToFetch: toFetch)
         
        guard contacts.count > 0 else{
            print("No contacts found")
            return
        }
         
        //only do this to the first contact matching our criteria
        guard let contact = contacts.first else{
            return
        }
         
        let req = CNSaveRequest()
        let mutableContact = contact.mutableCopy() as! CNMutableContact
        req.deleteContact(mutableContact)
         
        do{
            try appDelegate.contactStore.executeSaveRequest(req)
            print("Successfully deleted the Contact")
            self.statusLabel.text = "Successfully Deleted coderzheaven"
             
        } catch let e{
            print("Error = \(e)")
        }
         
    } catch let err{
        print(err)
        self.errorStatus()
    }
}

You can download the complete Source Code from here.