SWIFT 電話帳読み込み-実装コード1

開発検証環境:Xcode Ver9.4.1 動作検証OS:iOS11.0 言語:swift3 iOSの標準アプリ「連絡先」に登録されている連絡先データを取得する方法のまとめです。 なお、忘備録的に書いていますので、なぜそうするのかについて主に記載していく予定です。 連絡先から取り込んだデータはたいていDBに格納すると思います。それでこのアプリの中のPersonData型として取り込みます。

①Contacts.frameworkをリンク・インポート  連絡帳からデータを取得するにはContacts.frameworkが必要であるので

②連絡先のピッカーを管理するクラスを利用 CNContactPickerViewController

   // MARK: CNContactPickerDelegate methods
    func contactPicker(
        _ picker: CNContactPickerViewController,
        didSelect contacts: [CNContact]) {

// CNContact ピッカービューコントローラ
//このクラスは、連絡先のピッカービューを管理するコントローラオブジェクトを作成します。
このクラスを使用すると、連絡先ビューコントローラ()に表示されている連絡先のリストから1つ以上の連絡先(またはそのプロパティ)を選択できます。
ピッカーは、連絡先の単一選択と複数選択をサポートします。
2つ目の引数がcontacts: [CNContact] となっているのは当然、ユーザーがいくつか連絡帳から選択するので配列で渡しています。

// CNContactPikerViewController が[Done]で終了した時
        // 選択項目の有無によらず呼ばれる
        if contacts.count == 0 {
            return
}

//何も選択せずにユーザーがDoneを押したときの処理。 //このようにnilが発生する可能性はすべて潰さないといけません。

        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
      return
        }

// 発生することは無いAppDelegateクラスがないことは考えられないので、当たり前といえば当たり前。 ちなみにAppDelegateクラスはC言語のmain関数のようなもの。 //これはcoreDataの保存処理。 CoredataはAPPDelegateにあるpersisitconttainerにアクセスして保存処理をします。 f:id:happy_teeth_ago:20180625221622p:plain 参考サイト ここのサイトの説明はとてもわかりやすいです。

qiita.com

//guard節です。Guardは条件が偽のときに{}ないの文を処理します。if文と逆です。そして{}の処理を抜けるためにreturnが必要です。なので記載しています。

        let context:NSManagedObjectContext = appDelegate.persistentContainer.viewContext
        //保存する入れ物を準備
        var freeDatPerson: DatPerson?
//このアプリで利用する型のインスタンスを宣言
//配列からとりだしてループ回します。
        for contact in contacts {
            // DatPerson を取得し id を割り当てる。直前が未登録DatPersonならそれを流用する。
            let datPerson: DatPerson
            if freeDatPerson == nil {
         
                datPerson = DatPerson.newFormalOne()
                freeDatPerson = datPerson
        
       
            } else {
                datPerson = freeDatPerson!
            }
2)につづく

newFormalOne()は何をしているかというと
別関数を呼び出しこの関数は下記。
staticが使われている。staticはどんな時に使うかと言うと、ざっくり
1)生成などのインスタンス変数に依存しない処理
オブジェクトを複数作る必要性のなく、いろいろなところからアクセスされるような場合?
例えば、定数クラス。
2)自分でオブジェクトを生成しなくても、参照出来る点
例えば、オブジェクトを生成する意味がない定数クラスを毎回生成してからじゃないと定数を取得出来ないというようなことがなくなる。
以下は別クラスのfunc

    static func newFormalOne() -> DatPerson {
        let dat = newOneWithId()
        dat.fdate = NSDate()
        dat.lastMeeting = dat.fdate
        dat.name = dat.defaultName
        let meeting = DatMeeting.newOneWithId(dat)
        meeting.date = dat.fdate
        return dat
    }

ちゃんと作ってますね。 newOneWithId()は何をしているかというと これ 別関数です。 新しくオブジェクト作ってますね。

   /// id 割り当て済みの新規インスタンスを返す
    static func newOneWithId() -> DatPerson {
        let dat = DatPerson(context: Contract.managedObjectContext)
        Contract.makeStringPropertyEmpty(dat)
        dat.id = numberIdOfNewEntity(Contract.entityNameDatPerson)
        return dat
    }

どんどん行きます。Contract.managedObjectContextは
頻繁に使うものは関数化しているだけの話。
特にCoreDataは同じことを何度も書かないといけないので、、、
保存領域を用意しているだけですね。

    /// アプリが使用してる NamagedObjectContext
    static var managedObjectContext:NSManagedObjectContext {
        return persistentContainer.viewContext
    }

numberIdOfNewEntity関数は

/// id列を持つエンティティーの新レコード用に id番号を発行する(アプリ本体用)
fileprivate func numberIdOfNewEntity(
    _ entitiyName: String,
    datPerson: DatPerson? = nil ) -> Int64 {
    return numberIdOfNewEntity(
        Contract.managedObjectContext,
        entitiyName: entitiyName,
        datPerson: datPerson)
}
//ここから2)
            // 姓名,セイメイ,会社:全て空の時は DatPerson には登録しない
            datPerson.name = CNContactFormatter.string(from: contact, style: .fullName)
            datPerson.kana = CNContactFormatter.string(from: contact, style: .phoneticFullName)
            datPerson.company = contact.organizationName
            if (datPerson.name ?? "").isEmpty &&
                (datPerson.kana ?? "").isEmpty &&
                (datPerson.company ?? "").isEmpty {
                continue
            }
            // 住所,電話番号,email:登録順に複数(Home,Work,..等)存在するが
            // 最初(先頭)に登録されたものを採用する。
            if contact.postalAddresses.count > 0 {
                let address = contact.postalAddresses[0].value
                datPerson.jusho = CNPostalAddressFormatter.string(from: address, style: .mailingAddress)
                // ※ style:には何故か mailingAddress を設定する慣わしのようである
            }
            if contact.phoneNumbers.count > 0 {
                datPerson.tel = contact.phoneNumbers[0].value.stringValue
            }
            if contact.emailAddresses.count > 0 {
                datPerson.email = contact.emailAddresses[0].value as String
            }
            print("ContactsImporter.contactPicker() id:\(datPerson.id), name:\(datPerson.name ?? "--")")
            do {
                try context.save()
                freeDatPerson = nil
            } catch {
                // 次の datPerson 登録が成功する事は考えられない
                // したがって、失敗したら中止とする
                print(error)
                break
            }
        }
        // save()中止した DatPerson を返却する
        if freeDatPerson != nil {
            context.delete(freeDatPerson!)
        }
    }

以上