CoderzHeaven

February 23, 2016

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

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.

Comments are closed.