【Swift UIKit】CNContactで連絡先と連携させて情報を取得する方法!

【Swift UIKit】CNContactで連絡先と連携させて情報を取得する方法!

この記事からわかること

  • SwiftUIKitContactsフレームワークを使ってユーザー連絡先連携する方法
  • 連絡先の氏名誕生日などを取得するには?
  • CNContactCNContactFetchRequestクラス使い方
  • 取得できる情報のキー(CNKeyDescriptor)の種類
  • CNMutableContactCNSaveRequestを使った連絡先の追加方法
  • テーブルビューへの組み込み方法

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

SwiftのContactsフレームワークを導入して標準の連絡先との連携方法をまとめていきます。

Contactsフレームワークとは?

公式リファレンス:Contactsフレームワーク

ContactsフレームワークとはiOSアプリ開発において、ユーザーの連絡先情報に、連絡先への連携や操作を可能にできるユーザーインターフェースを提供しているフレームワークです。

Xcodeには既に導入されているフレームワークなのでimportを記述することで簡単に導入することが可能です。

import Contacts

info.plistへのキー追加

Contactsを用いることでユーザーの連絡先に、開発したアプリからアクセスすることができますが、その前に開発するアプリのinfo.plistへのキー:NSContactsUsageDescriptionの追加が必要になります。NSCalendarsUsageDescriptionとキーに入力するとPrivacy - Contacts Usage Descriptionと自動変換されます。

【Swift UIKit】CNContactで連絡先と連携させて情報を取得する方法!

キーを追加していないとアプリがクラッシュしてしまうので注意してください。

連絡先ではなく、カレンダーアプリやリマインダーなどにアクセスするためのEventKitフレームワークも用意されています。基本的な使い方は同じなので導入してみてください。

Contactsの仕組み

ContactsではCNContactStore(コンタクトストア)クラスとして連絡先へのアクセスをサポートしています。

class CNContactStore : NSObject

このコンタクトストアごとにアクセスへの許可や操作などを行います。複数インスタンス化することもできますが、アプリ内では1つのインスタンスを生成し、そのインスタンスごとに許可申請や操作をしないとエラーの元になるので注意してください。

let contactStore: CNContactStore = CNContactStore()

requestAccessで許可申請

連絡先へのアクセスはユーザーの許可がないとアクセスすることはできません。申請を出すにはインスタンス化したEKEventStoreからrequestAccessメソッドを呼び出してポップアップを表示させユーザーへ許可を申請します。

func authStatusContactRequest(){
        if CNContactStore.authorizationStatus(for: .contacts) == .notDetermined{
            contactStore.requestAccess(for: .contacts, completionHandler: { (granted, error) in
                if granted && error == nil {
                    print("許可")                }
            })
        }
    }

authorizationStatusでステータスを取得

許可申請を出す前に現在の許可状態を取得し、未許可であれば申請を出すようにします。authorizationStatusからCNAuthorizationStatus型の現在の許可状態が取得できるのでnotDetermined(未申請)の場合のみ申請を出すようにしておきます。

公式リファレンス:authorizationStatusメソッド

enum CNAuthorizationStatus : Int, @unchecked  Sendable {
    case authorized // 承認済
    case denied // 明示的に拒否
    case notDetermined // 未選択
    case restricted // 未承認
}

使用しやすくするために独自のContactControllerクラスを用意してContactsを定義しておきます。許可申請や連絡先の取得などを関数などにまとめて呼び出しやすいようにしておきました。

import Foundation
import Contacts
import UIKit

class ContactController {
    
    let contactStore: CNContactStore = CNContactStore()
    var contacts: [CNContact]?
    
    let keys = [CNContactGivenNameKey as CNKeyDescriptor,
                CNContactBirthdayKey as CNKeyDescriptor,
                CNContactFamilyNameKey as CNKeyDescriptor,
                CNContactPhoneNumbersKey as CNKeyDescriptor]
    init(){
        authStatusContactRequest()
        loadContacts()
    }
    
    func authStatusContactRequest(){
        if CNContactStore.authorizationStatus(for: .contacts) == .notDetermined{
            contactStore.requestAccess(for: .contacts, completionHandler: { (granted, error) in
                if granted && error == nil {
                    print("許可")
                }
            })
        }
    }
    
    func loadContacts() {
    }
    
    func addContact(given:String,family:String){
    }
}

連絡先データの取得

  1. 取得したい情報のキーを定義
  2. CNContactFetchRequestクラスにキーを渡して取得リクエストを作成
  3. enumerateContactsメソッドで連絡先を取得
  4. 一連の処理はバックグラウンドスレッドで実行
func loadContacts() {
    let globalQ = DispatchQueue.global(qos: .background)
    globalQ.async {
        
        let request = CNContactFetchRequest(keysToFetch: self.keys)
        var contacts:[CNContact] = []
        do {
            try self.contactStore.enumerateContacts(with: request) { contact, pointer in
                contacts.append(contact)
                print(contact.givenName)
                print(contact.familyName)
                print(contact.birthday)
                print(contact.phoneNumbers)
            }
        } catch {
            print("error")
        }
        self.contacts =  contacts
    }
}

連絡先取得の1連の操作はバックグラウンドスレッドの実行が公式よりオススメされているようです。

”連絡先ストアは、すべての I/O 操作をカプセル化し、連絡先とグループの取得と保存を担当します。連絡先ストア メソッドは同期的であるため、バックグラウンド スレッドで使用することをお勧めします。”

引用:Fetching contacts

取得したい情報のキーを定義

連絡先の情報を取得するにはあらかじめ取得したい情報のキーを定義しておきます。キーの全ての種類は以下の公式ページを参照してください。

公式リファレンス:Contact Keys

例えば以下は名前(姓名)と誕生日、電話番号を取得したい時のキー定義です。

let keys = [CNContactGivenNameKey as CNKeyDescriptor,
            CNContactBirthdayKey as CNKeyDescriptor,
            CNContactFamilyNameKey as CNKeyDescriptor,
            CNContactPhoneNumbersKey as CNKeyDescriptor]
    

CNContactFetchRequestクラス

公式リファレンス:CNContactFetchRequestクラス

let request = CNContactFetchRequest(keysToFetch: self.keys)

キー定義ができたらCNContactFetchRequestクラスを使ってリクエストオブジェクトを生成します。引数には取得したい連絡先情報のキーを渡します。

enumerateContactsメソッド

公式リファレンス:enumerateContactsメソッド

func enumerateContacts(
    with fetchRequest: CNContactFetchRequest,
    usingBlock block: (CNContact, UnsafeMutablePointer<ObjCBool>) -> Void
) throws

実際に連絡先を取得するにはenumerateContactsメソッドを使用します。引数に先程定義したリクエスト情報を渡し、引数block内でマッチする連絡先ごとに連絡先情報にアクセスできます。

throwsキーワードがついているのでtry文でエラーキャッチができるようにしておきます。

do {
    try self.contactStore.enumerateContacts(with: request) { contact, pointer in
        contacts.append(contact)
        print(contact.givenName)
        print(contact.familyName)
        print(contact.birthday)
        print(contact.phoneNumbers)
    }
} catch {
    print("error")
}

連絡先データの追加

  1. CNSaveRequestオブジェクトを生成
  2. CNMutableContactオブジェクトに個人情報を格納
  3. リクエストに個人情報を追加
  4. コンタクトストアからリクエストを実行

公式リファレンス:CNSaveRequest

公式リファレンス:CNMutableContact

func addContact(given:String,family:String){
    let request = CNSaveRequest()
    let contact = CNMutableContact()
    contact.givenName = given   // "名"
    contact.familyName = family // "姓"
    contact.phoneticGivenName = "なまえ"
    contact.phoneticFamilyName = "みょうじ"
    contact.phoneNumbers = [CNLabeledValue<CNPhoneNumber>(label: CNLabelPhoneNumberMain, value: CNPhoneNumber(stringValue: "000-1234-5678"))]
    contact.emailAddresses = [CNLabeledValue<NSString>(label: CNLabelHome, value: NSString(string: "ame@sample.com"))]
    request.add(contact, toContainerWithIdentifier: contactStore.defaultContainerIdentifier())
    do {
        print("追加")
        try contactStore.execute(request)
    } catch {
        print(error)
    }
}

テーブルビューに表示させてみる

これで連絡先の情報を取得/追加できるようになったのでテーブルビューに当てはめて表示させていきます。

'【Swift UIKit】EventKitでカレンダーを読み込む方法!保存や削除

import Foundation
import Contacts
import UIKit

class ContactController {
    
    let contactStore: CNContactStore = CNContactStore()
    var contacts: [CNContact]?
    
    let keys = [CNContactGivenNameKey as CNKeyDescriptor,
                CNContactBirthdayKey as CNKeyDescriptor,
                CNContactFamilyNameKey as CNKeyDescriptor,
                CNContactPhoneNumbersKey as CNKeyDescriptor]
    init(){
        authStatusContactRequest()
        loadContacts()
    }
    
    func authStatusContactRequest(){
        if CNContactStore.authorizationStatus(for: .contacts) == .notDetermined{
            contactStore.requestAccess(for: .contacts, completionHandler: { (granted, error) in
                if granted && error == nil {
                    print("許可")
                }
            })
        }
    }
    
    func loadContacts() {
        let globalQ = DispatchQueue.global(qos: .background)
        globalQ.async {
            
            let request = CNContactFetchRequest(keysToFetch: self.keys)
            var contacts:[CNContact] = []
            do {
                try self.contactStore.enumerateContacts(with: request) { contact, pointer in
                    contacts.append(contact)
                    print(contact.givenName)
                    print(contact.familyName)
                    print(contact.birthday)
                    print(contact.phoneNumbers)
                }
            } catch {
                print("error")
            }
            self.contacts =  contacts
        }
    }
    
    func addContact(given:String,family:String){
        let request = CNSaveRequest()
        let contact = CNMutableContact()
        contact.givenName = given   // "名"
        contact.familyName = family // "姓"
        contact.phoneticGivenName = "なまえ"
        contact.phoneticFamilyName = "みょうじ"
        contact.phoneNumbers = [CNLabeledValue<CNPhoneNumber>(label: CNLabelPhoneNumberMain, value: CNPhoneNumber(stringValue: "000-1234-5678"))]
        contact.emailAddresses = [CNLabeledValue<NSString>(label: CNLabelHome, value: NSString(string: "ame@sample.com"))]
        request.add(contact, toContainerWithIdentifier: contactStore.defaultContainerIdentifier())
        do {
            print("追加")
            try contactStore.execute(request)
        } catch {
            print(error)
        }
    }
}

import UIKit

class ContactViewController: UIViewController ,UITableViewDataSource,UITableViewDelegate {
    let contactController = ContactController()
    
    @IBOutlet  var givenNametextField:UITextField!
    @IBOutlet  var familyNameField:UITextField!
    @IBOutlet  var entryBtn:UIButton!
    @IBOutlet  var tableView:UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        entryBtn.addTarget(self, action: #selector(self.entryBtnTapped), for: .touchUpInside)
        
    }
    
    @objc  func entryBtnTapped(){
        if givenNametextField.hasText && familyNameField.hasText {
            contactController.addContact(given: givenNametextField.text!, family: familyNameField.text!)
        }
        tableView.reloadData()
        
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return contactController.contacts?.count ?? 0
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .default, reuseIdentifier: "myCell")
        let name = contactController.contacts![indexPath.row].givenName + " " + contactController.contacts![indexPath.row].familyName
        cell.textLabel!.text = name
        return cell
        
    }    
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index