swift（iOS10）で連絡先アプリから連絡先データを取得する


- 開発検証環境：Xcode Ver8.0（macOS Sierra 10.12.1）
- 動作検証OS：iOS10.0
- 言語：swift3

iOSの標準アプリ「連絡先」に登録されている連絡先データを取得する方法のまとめです。
なお、
・この方法はiOS10向けアプリの製作を想定しているのでそれ以前のVersionを
　フォローした作りにはなっていません。
・連絡先アプリのユーザーインターフェイスにアクセスして連絡先データを取得する方法もありますが、
　今回は連絡先データに直接アクセスする方法となります。
　なお、連絡先のユーザーインターフェイスにアクセスする方法に関しては
　下記リンク先を参考にさせていただきました。
　[iOS 9から追加されたContacts Frameworkを使って連絡先アプリのデータを取得してみる](http://qiita.com/koogawa/items/44ce2e1fb127b884d835)
　

---
①Contacts.frameworkをリンク・インポート
　連絡帳からデータを取得するにはContacts.frameworkが必要であるので
　
　.xcodeprojファイル > General > Linked Frameworks and Libraryに
　Contacts.frameworkを登録する
　
　![スクリーンショット 2016-11-02 21.51.11.png](https://qiita-image-store.s3.amazonaws.com/0/143342/535ac059-db80-8b2f-de27-5589376672e4.png "スクリーンショット 2016-11-02 21.51.11.png")

その上でCNContactStore等、Contentrs.frameworkに関連するクラスを使うクラスで
下記のようにContacts.frameworkをインポート

```swift
import Contacts
```

---
②プライバシー設定に連絡先アプリへのアクセスを設定する

　この設定をしないで、Contacts.framework関連のクラスを利用すると
　なんの前触れもなくアプリが落ちて、原因も特定しずらいので注意です

　設定は簡単でinfo.plistに項目を追加し
　・keyに「Contacts Usage Description」 
　　対するvalueにメッセージを出すときの文言を書きます。
　　　　→下記例ではvalue「連絡先にアクセスします」を設定しています。　
  
  ![スクリーンショット 2016-11-02 20.58.31.png](https://qiita-image-store.s3.amazonaws.com/0/143342/47839382-fb51-0446-5955-32b660f2d7a7.png "スクリーンショット 2016-11-02 20.58.31.png")
  
  　上記スクリーンショットでは　Contacts Usage Description　の前に
  　Privacy - が先頭につくのですが Contacts Usage Description を入力すると
  　自動的に記載が追加されるので　Privacy - の入力は必要ありません。
  　
  
  　なお余談ですが、上記スクリーンショットでもそうですが
  　電話を発信するアプリなのであれば使用デバイスの聖言項目として
  　Required device capabilities > Item > telephony
  　と設定する事でアプリのインストール可能なデバイスが電話発信機能をもつものに制限されます。
  　
 プライバシー設定に関しては下記リンク先を参考にさせていただきました。
 [プライバシー設定をplistにしとかないと落ちる](http://galakutaapp.blogspot.jp/2016/09/plist.html)
 
---
③連絡先へのアクセスが許可されているか確認する

　いよいよ連絡先データへアクセス・・・　といきたいところですが
　②で設定したプライバシー設定のようにOS側で制御されている、アプリに対する
　連絡先データへのアクセス権限がどうなっているかを確認する手順が必要となります。
　
　この処理を記載するクラスには①に記載されている import Contacts を書いておきます。

```swift

            // 連絡帳アクセス可否状態を取得
            let status = CNContactStore.authorizationStatus(for: CNEntityType.contacts)
            
            switch (status) {
                
            // まだダイアログから選択を行っていない または　ペアレンタルコントロールや、機能制限によりアクセス不可
            case CNAuthorizationStatus.notDetermined,CNAuthorizationStatus.restricted:
                
                // このアプリが連絡先を使ってもいいかをユーザーが選択していない場合に、連絡先の使用を許可するか禁止するかを選択するメッセージボックスを表示
                // クロージャー内、循環参照によるメモリーリークに注意！
                let store = CNContactStore.init()
                store.requestAccess(for: CNEntityType.contacts, completionHandler: { (granted, Error) in
                    
                    // 利用可能
                    if granted {
                        
                        // クロージャー内の処理なのでメッセージを表示する等、画面に関連する処理を行う場合はメインスレッドで処理を行うようにしましょう
                        DispatchQueue.main.async {
                        }
                        
                    // 利用不可能
                    } else {
                    
                        // クロージャー内の処理なのでメッセージを表示する等、画面に関連する処理を行う場合はメインスレッドで処理を行うようにしましょう
                        DispatchQueue.main.async {  
                        }
                    }
                })
                
            // 拒否が選択されている
            case CNAuthorizationStatus.denied:
      
            // 利用可能
            case CNAuthorizationStatus.authorized:

            }
     

```

　処理の流れとして
　・利用可能→実際に連絡先データを取得する処理に移りましょう
　・利用不可能・拒否が選択されている
　　→その旨、ユーザーにアラート等を表示する
　という形になりますが、そこらへんはアプリの実装に応じて柔軟に対応してください。
　
　なお、この処理に関しては主に下記のページを参考にさせていただきました。
　[AddressBook / Contacts.frameworkを利用してアドレス帳のデータを利用する](http://qiita.com/Takumi_Mori/items/751b22217433390f1abc)

---
④連絡先データを取得する
　③で連絡先データが利用可能となったらいよいよ連絡先データを取得します
　なお、連絡先データを取得するクラスでも①に記載されている import Contacts を書いておきます。

```swift

        // 連絡先を取得するクラスのインスタ巣を作成
        let store = CNContactStore.init()

        // 連絡帳の1つ1つのデータを収める空の配列 people を準備
        var people = [CNContact]()
        

        do {
            // 連絡先データベースからここでは苗字・名前・電話番号情報を取得
            try store.enumerateContacts(with: CNContactFetchRequest(keysToFetch: [CNContactGivenNameKey as CNKeyDescriptor,
                                                                                  CNContactFamilyNameKey as CNKeyDescriptor,
                                                                                  CNContactPhoneNumbersKey as CNKeyDescriptor])) {
                (contact, cursor) -> Void in
                
                // 電話番号が保持されている連絡先だったら
                if (!contact.phoneNumbers.isEmpty){
                
                   // 取得したデータをpeople に収める
                    people.append(contact)
                }
            }
        }
        catch{
            print("連絡先データの取得に失敗しました")
        }
        
        // peopleに取得した連絡先の数だけループ
        for humen in people{
        
            // 連絡先が保持している電話番号の数だけループ
            for number in humen.phoneNumbers {
            
                // 「電話：電話番号　名前：苗字 ま前」のフォーマットで連絡先データが出力される
                print("電話：\((number.value as CNPhoneNumber).stringValue) 名前：\(humen.familyName) \(humen.givenName)")
            }
        }


```

　上記例では　連絡先アプリに登録されている連絡先から電話番号と名前を取得し、
　それをログ出力するというソースコードになっていますが
　実際は取得したデータを構造体の配列などに収めるなどして表示する　という感じで使うことになると思います。

　なお、連絡先データベースから取得可能なデータのキー 一覧はAPIリファレンス等から取得は
　できなかったのですが実際にソースを記入した上でCNContactFamilyNameKeyなどのキーを選択して、
　右クリックメニューからJump to Definitionを選択すると定義されているソースに
　その一覧を見ることができるのでそこで確認してみてください。
　
　取得した連絡先データは
　[Apple API Refarence - CNContact]
(https://developer.apple.com/reference/contacts/cncontact)
　のプロパティーなどを参照して、目的のデータ（名前や電話番号）を取得してみてください

　ここでは下記リンク先を参考にさせていただきました。
　 [Swift iOS9 New Contacts Framework - How to retrieve only CNContact that has a valid email address?](http://stackoverflow.com/questions/32665605/swift-ios9-new-contacts-framework-how-to-retrieve-only-cncontact-that-has-a-va)