//
// 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