iOS9 之 Contacts

在之前開發者訪問用戶的聯繫人在他們的iOS設備上使用C API,那是個蠻痛苦的事情。直到iOS8還一直在使用,不過如今Apple推出了兩個功能強大的面相對象的框架來管理用戶聯繫人。ContactsContactsUI接下來,咱們就來看看如何使用這些框架。git

博客原文swift

初始項目數組

  1. 使用ContactsUI框架來顯示和選擇聯繫人。安全

  2. 將聯繫人添加到用戶的聯繫人列表。閉包

  3. 搜索用戶的聯繫人,並使用NSPredicate篩選。app

Getting started

運行你下載的初始化項目,你將看到一個聯繫人的列表。框架

Converet friends to CNContacts

CNContact這個類中包含了不少的聯繫人的屬性,好比說,givenName,familyName,emailAddress,imageData等等。。。async

打開咱們項目中的Friend.swift添加下邊代碼:ide

import Contacts函數

如今呢,擴展這個類,添加一個計算屬性

extension Friend{

var contactValue:CNContact{
    //1 建立一個不帶參數的 `CNMutable` 聯繫實例
    let contact = CNMutableContact()
    //2更新對應的屬性
    contact.givenName = firstName
    contact.familyName = lastName
    //3 emailAddresses 是 CNLabeledValue 對象的數組。這意味着每一個電子郵件地址都有一個對應的標籤。有多種類型可供接觸的標籤,但在這種狀況下,你堅持使用CNLabelWork。

    contact.emailAddress = [CNLabeledValue(label:CNLabelWork,value:workEmail)]
    //4若是有朋友的圖片,聯繫人的圖像數據用JPEG格式
    if let profilePicture = profilePicture{
        let imageData = UIImageJPEGRepresentation(profilePicture, 1)
        contact.imageData = imageData
    }
    //5返回複製的副本
    return contact.copy() as! CNContact
    }
}

提示:CNMutableContactCNContact的可變類型,CNContact的屬性只能夠讀,而CNMutableContact的屬性能夠改變。所以,你建立一個可變的聯繫人,設置其屬性,而後完成之後,返回一個不可變的副本。須要注意的是CNContacat是線程安全的,而CNMutableContact不是。

Showing the contact's information

FriendsViewController.swift添加下邊的代碼:

import Contacts

import COntactsUI

extension FriendsViewController{
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
        
        //1 使用所選單元格的索引路徑來得到所選擇的朋友,並將其轉換到CNContact的一個實例
        let friend = friendsList[indexPath.row]
        let contact = friend.contactValue
        //2使用forUnknownContact來初始化一個`CNContactViewController`,由於聯繫人不在用戶的聯繫人存儲中。你還能夠自定義導航欄和標籤欄的屬性。
        let contactViewController = CNContactViewController(forUnknownContact: contact)
        contactViewController.navigationItem.title = "Profile"
        contactViewController.hidesBottomBarWhenPushed = true
        //3設置這兩個屬性爲false,那麼用戶智能查看聯繫人的信息
        contactViewController.allowsActions = false
        contactViewController.allowsEditing = false
        //4
        navigationController?.pushViewController(contactViewController, animated: true)
    }
}

如今你運行你的應用程序,點擊一個 tableView cell 你將看到ContactsUI framework將會展示朋友的信息。

你不能添加更多的朋友好友列表?您可使用ContactsUI類CNContactPickerViewController讓你的用戶選擇聯繫人的應用程序來使用。

Picking your friends

帶開Main.storyboardfriendsViewController添加一個Bar Button Item 在導航欄的右側,而且設置他的image屬性爲AddButton


接着給他鏈接一個方法addFriends


在這個方法中寫:

@IBAction func addFriends(sender: UIBarButtonItem) {
        let contactPicker = CNContactPickerViewController()
        presentViewController(contactPicker, animated: true, completion: nil)
    }

而後你運行你的程序,點擊按鈕,就會看到下邊的樣子:

目前,用戶沒法導入聯繫人。當用戶選擇一個聯繫人,選擇器視圖控制器只顯示有關聯繫人的詳細信息。爲了解決這個問題,你須要採起的CNContactPickerDelegate方法的優點。

Conforming to CNContactPickerDelegate

CNContactPickerDelegate有五個可選的方法,如今你可能有興趣的是contactPicker(:didSelectContacts)

extension FriendsViewController:CNContactPickerDelegate{
    func contactPicker(picker: CNContactPickerViewController, didSelectContacts contacts: [CNContact]) {
        
    }
}

如今FriendsViewController遵循 CNContactPickerDelegate這個協議,你已經有了一個空的contactPicker(_:didSelectContacts:)方法,接下來要作的事情有:

  1. 從CNContscts建立一個新的朋友實例

  2. 增長新的朋友實例給你的朋友列表

Creating a friend from a CNContact

打開Friend.swift添加以下的初始化方法

extension Friend{
    init(contact:CNContact){
        firstName = contact.givenName
        lastName = contact.familyName
        workEmail = contact.emailAddresses.first?.value as! String
        if let imagedata = contact.imageData{
            profilePicture = UIImage(data: imagedata)
        }else{
            profilePicture = nil
        }
    }
}

當您設置workEmail強制解開找到的第一個電子郵件地址。因爲RWConnect使用電子郵件,以保持與您的朋友保持聯繫,全部的聯繫人必須有電子郵件地址。若是強行展開這樣使你焦躁不安,不用擔憂- 你將在之後解決它!

Adding new friends to the friends List

接下來完成你的contactPicker(_:didSelectContacts:)這個代理方法

extension FriendsViewController:CNContactPickerDelegate{
    func contactPicker(picker: CNContactPickerViewController, didSelectContacts contacts: [CNContact]) {
        let newFriends = contacts.map{Friend(contact: $0)}
        for friend in newFriends{
            if !friendsList.contains(friend){
                friendsList.append(friend)
            }
        }
        tableView.reloadData()
    }
}

找到你的addFriends(_:)方法,在 presentViewController(_:animated:completion:)以前添加下邊的代碼

contactPicker.delegate = self

如今你來運行你的程序,來添加新的聯繫人到你的程序列表中。

你覺得就這樣完了麼?NONONO還有一個小bug咱們沒有解決,還記得咱們是強制解析 emailAddress,若是你選的聯繫人沒有郵箱地址的話,這個程序會crash掉。咱們要來解決這個問題。

咱們在這裏解決的辦法是讓用戶不能選擇沒有電子郵件的聯繫人。

presentViewcontroller(:animated:completion)以前添加下面的代碼:

contactPicker.predicateForEnablingContact = NSPredicate(format: "emailAddress.@count > 0")

聯繫人選取器的predicateForEnablingContact 屬性可讓你決定哪些聯繫人能夠選擇。

當你再次運行項目的時候,點擊添加按鈕,你會看到,若是沒有電子郵件地址的聯繫人顯示爲灰色。

Saving friends to the user's contacts

咱們給單元格添加一個左滑的動做,會顯示獎聯繫人添加到聯繫人存儲。

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
        let createContact = UITableViewRowAction(style: .Normal, title: "Create Contact") { (rowaction, indexPath) -> Void in
            //todo: add the contact
        }
        createContact.backgroundColor = BlueColor
        return [createContact]
    }

在你訪問或者修改用戶的聯繫人,你必須獲得用戶的許可。

在這裏你可能會疑惑,在上邊使用 CNContactPickerViewController時,你並無索取用戶的許可,這是爲何呢。這是由於CNContactPickerViewController是一個out-of-process。當用戶選擇了聯繫人的時候,就暗示了你容許使用他們的聯繫人。

Asking for permission

let contactStore = CNContactStore()
            contactStore.requestAccessForEntityType(CNEntityType.Contacts, completionHandler: { (userFrantedAccess, _) -> Void in
                
            })

在這裏你建立了一個CNContactStore實例,這表明用戶的地址薄中包含全部它們的聯繫人,一旦你初始化聯繫人存儲,調用實例方法 requestAccessForEntityType完成處理須要一個布爾值,指示用戶授予權限來訪問他們的contacts。

Add the following method to FriendsViewController:

func presentPremissionErrorAlert(){
        dispatch_async(dispatch_get_main_queue()) { () -> Void in
            let alert = UIAlertController(title: "Could Not Save Contact", message: "How an I supposed to add the contact if you didn't give me premission", preferredStyle: UIAlertControllerStyle.Alert)
            let openSettingsAction = UIAlertAction(title: "Settings", style: .Default, handler: { (alert) -> Void in
                //打開設置
                UIApplication.sharedApplication().openURL(NSURL(string: UIApplicationOpenSettingsURLString)!)
            })
            let dismissAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
            
            alert.addAction(openSettingsAction)
            alert.addAction(dismissAction)
            
            self.presentViewController(alert, animated: true, completion: nil)
        }
    }

上邊的方法提供了一個警告,表示應用程序沒法保存聯繫人用戶。經過UIApplicationOpenSettingsURLString來打開設置應用程序。

須要注意的是咱們把上邊的這個警告放在了主線程裏來執行,這樣能夠更快的顯示出警告。

咱們完成咱們的requestAccessForEntityType閉包。

guard userFrantedAccess else{
                    self.presentPremissionErrorAlert()
                    return
                }

如今在你的模擬器中來運行項目

若是你選擇了不容許的話將會出現下邊的警告

若是你選擇了設置,那麼會打開設置應用。

Saving friends to contacts

咱們來建立一個保存聯繫人的方法。

func saveFriendToContacts(friend:Friend){
        //1 首先呢咱們須要一個可變的CNContact
        let contact = friend.contactValue.mutableCopy() as! CNMutableContact
        //2 新建一個CNSaveRequest 來更新或者刪除聯繫人CNContactStore
        let saveRequest = CNSaveRequest()
        //3 接着告訴CNSaveRequest你想把新的朋友添加到用戶的聯繫人
        saveRequest.addContact(contact, toContainerWithIdentifier: nil)
        do { //4 嘗試着保存要求,若是該方法成功,則繼續執行,你能夠假設保存請求成功,不然拋出一個錯誤
                let contactStore = CNContactStore()
                try contactStore.executeSaveRequest(saveRequest)
            //show success alert
        }catch{
            //show failure alert
        }
    }

接下來完成show success alertshow failure alert

//show success alert
            dispatch_async(dispatch_get_main_queue()) {
                
                let successAlert = UIAlertController(title: "Contacts Saved", message: "nil", preferredStyle: .Alert)
                
                successAlert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
                
                self.presentViewController(successAlert, animated: true, completion: nil)
            }
//show failure alert
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                let failureAlert = UIAlertController(title: "Could Not Save Contact", message: "An Unknown error occurred.", preferredStyle: .Alert)
                failureAlert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
                self.presentViewController(failureAlert, animated: true, completion: nil)
            })

咱們在點擊了cell左滑的按鈕以後來執行這個方法

guard userFrantedAccess else{
                   self.presentPremissionErrorAlert()
                   return
               }
               
               let friend = self.friendsList[indexPath.row]
               self.saveFriendToContacts(friend)

咱們在運行項目以前須要重置一下模擬器。

以後從新運行,來保存你的朋友到你通信錄裏

你能夠到你的通信錄裏看到你新添加的朋友

細心的同窗可能會發現,若是你屢次添加一個聯繫人的話也能夠成功。咱們要阻止這樣的狀況發生。

Checking for existing contacts

添加下面的代碼到saveFriendToContacts函數的最上邊

//1  
        let contactFormatter = CNContactFormatter()
        //2 格式化基於聯繫人給出的姓名字符串
        let contactName = contactFormatter.stringFromContact(friend.contactValue)!
        //3 用於搜索是否存在上邊的姓名
        let predicateForMatchingName = CNContact.predicateForContactsMatchingName(contactName)
        //4 
        
        let matchingContacts = try! CNContactStore().unifiedContactsMatchingPredicate(predicateForMatchingName, keysToFetch: [])
        //5  只保存沒有匹配到的聯繫人
        guard matchingContacts.isEmpty else{
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                let alert = UIAlertController(title: "Contact Already Exists", message: nil, preferredStyle: .Alert)
                alert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
                self.presentViewController(alert, animated: true, completion: nil)
            })
            return
        }

完結!

相關文章
相關標籤/搜索