Contact.framework联系人相关操作

//

//  ViewController.swift

//  ContactClean

//

//  Created by zhangzb on 2019/7/31.

//  Copyright © 2019 zhangzb. All rights reserved.

//

/*

1、引入Contacts框架

2、检查授权状态

3、获取联系人

4、展示、增删改查操作

5、info.plist增加字段

*/

import UIKit

// 引入Contacts框架

import Contacts

class ViewController: UIViewController {

    // 所有联系人都是CNContact类型的数据,所以直接使用系统的CNContact实体类

    var contacts = [CNContact]()

    @IBOutlet var tableView: UITableView!


    override func viewDidLoad() {

        super.viewDidLoad()

        // 检查状态

        if self.checkAuthStatus() {

            // 如果已授权,那么直接拉取数据

            self.requestAllContacts()

        }

    }


    /// 检查授权状态,true已授权,false未授权

    func checkAuthStatus() -> Bool {

        weak var weakSelf = self

        // 获取授权状态

        switch CNContactStore.authorizationStatus(for: .contacts) {

        // 不允许,没有权限

        case .denied:

            let alertController = UIAlertController(title: "未授权", message: "您关闭了App访问通讯录的权限,请先去开启后重试", preferredStyle: .alert)

            alertController.addAction(UIAlertAction(title: "去开启", style: .default, handler: { (_) in

                weakSelf?.goToSetting()

            }))

            alertController.addAction(UIAlertAction(title: "取消", style: .cancel, handler: { (_) in


            }))

            self.present(alertController, animated: true, completion: nil)

        // 未授权,restricted(因为一些原因没有授权)

        case .notDetermined, .restricted:

            // 申请授权

            CNContactStore().requestAccess(for: .contacts) { (success, error) in

                if success, error == nil {

                    let alertController = UIAlertController(title: "成功", message: "授权成功", preferredStyle: .alert)

                    alertController.addAction(UIAlertAction(title: "确定", style: .default, handler: { (_) in

                        // 由于会优先返回false,所以第一次授权弹窗时不会刷新数据,在授权成功之后需要重新调用刷新方法

                        weakSelf?.requestAllContacts()

                    }))

                    weakSelf?.present(alertController, animated: true, completion: nil)

                } else {

                    let alertController = UIAlertController(title: "错误", message: "授权失败", preferredStyle: .alert)

                    alertController.addAction(UIAlertAction(title: "确定", style: .cancel, handler: { (_) in


                    }))

                    weakSelf?.present(alertController, animated: true, completion: nil)

                }

            }

        // 已允许

        case .authorized:

            print("Authorized")

            return true

        // CNAuthorizationStatus 可能会在将来添加状态,所以Apple要求写@unknown default状态,试试注掉会报警告

        // Switch covers known cases, but 'CNAuthorizationStatus' may have additional unknown values, possibly added in future versions

        @unknown default:

            let alertController = UIAlertController(title: "错误", message: "授权失败", preferredStyle: .alert)

            alertController.addAction(UIAlertAction(title: "确定", style: .cancel, handler: { (_) in

            }))

            weakSelf?.present(alertController, animated: true, completion: nil)

        }

        // 只有在允许的情况下才返回true

        return false

    }


    /// 刷新数据

    func requestAllContacts() {

        self.contacts.removeAll()

        weak var weakSelf = self

        let contactStore = CNContactStore()

        do {

            // 此处列举需要获取的联系人数据的key,eg. CNContactGivenNameKey

            try contactStore.enumerateContacts(with: CNContactFetchRequest(keysToFetch: [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey] as [CNKeyDescriptor])) { (contact, stop) in

                weakSelf?.contacts.append(contact)

            }

            self.tableView.reloadData()

        } catch {

            print(error)

        }


    }


    // 跳转设置页面

    func goToSetting() {

        if let settingUrl = URL(string: UIApplication.openSettingsURLString), UIApplication.shared.canOpenURL(settingUrl) {

            UIApplication.shared.open(settingUrl)

        }

    }


    // 增加一个

    @IBAction func addOne() {

        // 创建一个新的contact

        let contact = CNMutableContact()

        contact.givenName = "aaaaa\(arc4random_uniform(100))"

        contact.familyName = "bb\(arc4random_uniform(100))"

        var phones = [CNLabeledValue<CNPhoneNumber>]()

        for _ in 0...arc4random_uniform(5) {

            let phoneValue = CNLabeledValue(label: "手机", value: CNPhoneNumber(stringValue: "130000\(arc4random_uniform(99999))"))

            phones.append(phoneValue)

        }

        contact.phoneNumbers = phones


        // 创建保存saveRequest

        let saveRequest = CNSaveRequest()

        saveRequest.add(contact, toContainerWithIdentifier: nil)

        self.excute(saveRequest: saveRequest)

    }


    // 删

    func deleteOne(contact: CNContact) {

        // 创建删除saveRequest

        let saveRequest = CNSaveRequest()

        saveRequest.delete(contact.mutableCopy() as! CNMutableContact)

        self.excute(saveRequest: saveRequest)

    }


    // 改

    func updateOne(contact: CNContact) {

        // 修改对应的联系人信息

        let mutableContact = contact.mutableCopy() as! CNMutableContact

        mutableContact.givenName = "aaaaa\(arc4random_uniform(100))"

        mutableContact.familyName = "bb\(arc4random_uniform(100))"

        var phones = [CNLabeledValue<CNPhoneNumber>]()

        for _ in 0...arc4random_uniform(5) {

            let phoneValue = CNLabeledValue(label: "手机", value: CNPhoneNumber(stringValue: "130000\(arc4random_uniform(99999))"))

            phones.append(phoneValue)

        }

        mutableContact.phoneNumbers = phones


        // 创建修改saveRequest

        let saveRequest = CNSaveRequest()

        saveRequest.update(mutableContact)

        self.excute(saveRequest: saveRequest)

    }


    // 执行request

    func excute(saveRequest: CNSaveRequest) {

        do {

            try CNContactStore().execute(saveRequest)

            print("保存成功")

            self.requestAllContacts()

        } catch {

            print(error)

        }

    }

}

// 联系人cell

class CCContactCell: UITableViewCell {

    /// 姓名

    @IBOutlet var nameLabel: UILabel!

    /// 手机号码

    @IBOutlet var phoneLabel: UILabel!


    func updateUI(contact: CNContact) {

        self.nameLabel.text = contact.givenName + contact.familyName

        var phones = [String]()

        for phone in contact.phoneNumbers {

            phones.append(phone.value.stringValue)

        }

        self.phoneLabel.text = phones.joined(separator: ",")

    }

}

extension ViewController: UITableViewDataSource, UITableViewDelegate {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return self.contacts.count

    }


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        var cell = tableView.dequeueReusableCell(withIdentifier: "ContactCell") as? CCContactCell

        if cell == nil {

            cell = CCContactCell(style: .default, reuseIdentifier: "ContactCell")

        }

        cell?.updateUI(contact: self.contacts[indexPath.row])

        return cell ?? CCContactCell()

    }


    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        // 点击cell时更新联系人的信息

        self.updateOne(contact: self.contacts[indexPath.row])

    }


    func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {

        return .delete

    }


    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {

        // 删除联系人的信息

        self.deleteOne(contact: self.contacts[indexPath.row])

    }

}

最后,别忘了在项目info.plist中添加NSContactsUsageDescription

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352