CallKitを使用して番号を決定します



CRMに57,000の連絡先がある場合、人々はそれらをiPhoneに手動で書き込む気はありません。 別のアプリケーションで連絡先を検索できるだけでなく、着信した人物の名前を表示できる、よりエレガントなソリューションを見つける必要があります。 私たちは長い間グーグルで検索してから、WWDCを使用したCallKitフレームワークの発表を思い出しました。 このトピックに関する情報はそれほど多くありませんでした。簡潔なドキュメント 、Habré に関する記事であり、単一のステップバイステップガイドではありません。 このギャップを埋めたいです。 簡単なアプリケーションを作成する例を使用して、何千もの数字を識別するためにCallKitを教える方法を紹介します。

1つの番号を決定します


最初に、1つの数値を決定してみましょう。

空のプロジェクトから始めましょう。 TouchInAppという名前のシングルビューアプリケーションを作成します。

拡張子を追加して番号を決定します。 [Xcode]メニューから、[ファイル]> [新規]> [ターゲット...]を選択します。[アプリケーション拡張機能]セクションで、[ディレクトリ拡張機能を呼び出す]を選択し、[次へ]をクリックします。


[製品名]フィールドに「TouchInCallExtension」と入力し、[完了]をクリックします。 表示されるアラートで、[キャンセル]をクリックします。

電話の発信元となるテスト用の電話を既に準備していることを願っています。 そうでなければ、今がその時です。

プロジェクトナビゲータで、TouchInCallExtensionを展開し、CallDirectoryHandler.swiftを開きます。 addIdentificationPhoneNumbers関数を見つけます。 そこでは、 phoneNumberslabels配列が表示されます。 phoneNumbersから番号を削除し、そこにテスト番号を入力します。 labels配列の内容を削除し、「テスト番号」を入力します。

次のようなものが得られます。

 private func addIdentificationPhoneNumbers(to context: CXCallDirectoryExtensionContext) throws { let phoneNumbers: [CXCallDirectoryPhoneNumber] = [ 79214203692 ] let labels = [ "Test number" ] for (phoneNumber, label) in zip(phoneNumbers, labels) { context.addIdentificationEntry(withNextSequentialPhoneNumber: phoneNumber, label: label) } } 

CXCallDirectoryPhoneNumberは、 Int64単なるtypealiasです。 番号は7XXXXXXXXXXの形式である必要があります。つまり、最初に国コード(国呼び出しコード)、次に番号自体です。 ロシアのコードは+7なので、この場合は7と記述します。

デバイスにアプリケーションをインストールし、すぐに閉じます。 まだ何もする必要はありません。 [電話の設定]> [電話]> [通話のブロックと識別]に移動します。 そこでTouchInAppアプリを見つけて、呼び出しを識別してブロックします。 アプリケーションがリストにすぐに表示されないことがあります。 この場合、設定を閉じ、アプリケーションを再度開いて閉じてから、もう一度試してください。


スイッチをオン状態にするaddIdentificationPhoneNumbers 、以前に追加した内線番号からaddIdentificationPhoneNumbersaddIdentificationPhoneNumbersれ、そこから連絡先が読み取られます。

テスト番号からデバイスに呼び出します。 数を決定する必要があります。


数千の数字を決定します


もちろん、これはすべて素晴らしいことですが、これはほんの1つの数字です。 そして、記事の冒頭で、何千もの連絡先について話しました。 明らかに、すべてを手動でphoneNumbersおよびlabels配列に書き換えることはありません。

したがって、拡張機能に連絡先を追加する必要があります。 アプリケーションからこれを行うことはできません。 reloadExtension関数のみを呼び出すことができます。 reloadExtension関数を呼び出すと、 reloadExtensionが呼び出されaddIdentificationPhoneNumbers 。 彼女については少し後で話しましょう。

いずれにしても、アプリケーションは連絡先にアクセスできます。 それらは特定の形式ですぐに配信されるか、APIへのリクエストに応じて受信されるか、それ以外は問題ではありません。 拡張機能が何らかの形でこれらの連絡先を取得する必要があることが重要です。

ちょっと脱線して、ちょっとしたアナロジーを描いてみましょう。 あなたが猫を飼っていると想像してください。 ある場合は、想像できません。 あなたは朝目を覚ますと彼を養います。 どうしますか? おそらく、食べ物をボウルに注いでください。 そしてすでにそこから猫は食べます。

ここで、Call Directory Extensionが猫であり、あなたがアプリケーションであると想像してください。 また、Directory Directory Extensionを使用して連絡先をフィードする必要があります。 この場合、ボウルの役割を果たします。ボウルは、連絡先で満たす必要があり、その後どの拡張機能からそれらを消費しますか? 残念ながら、多くのオプションはありません。 拡張機能の実行中はリソースが非常に限られているため、Core DataまたはSQLiteは使用できません。


addIdentificationPhoneNumbers関数を編集したとき、おそらくコメントに気付いたでしょう。 「数字は数字の昇順で提供されなければならない」と述べています。 データベースからの選択のソートは、リソースを大量に消費して展開できません。 したがって、データベースを使用するソリューションは私たちには適していません。

残っているのは、ファイルを使用することだけです。 実装を簡単にするために、次の形式のテキストファイルを使用します。


この形式を使用しても、最適なパフォーマンスは得られません。 ただし、これにより、バイナリファイルの操作に没頭する代わりに、主要なポイントに集中できます。

残念ながら、アプリケーションと拡張機能の両方から1つのファイルを取得してアクセスすることはできません。 ただし、App Groupsを使用すると、可能になります。

アプリグループを使用して連絡先を共有する



App Groupにより、アプリケーションと拡張機能は共有データにアクセスできます。 詳細については、 Appleのドキュメントを参照してください。 これで作業したことがない場合、それは怖くない、今私はそれを設定する方法を教えます。

プロジェクトナビゲータで、プロジェクトをクリックします。 ターゲットアプリケーションを選択し、[機能]タブに移動して、アプリグループを有効にします。 グループ「group.ru.touchin.TouchInApp」を追加します。 ここでのロジックは、バンドル識別子と同じです。 グループのプレフィックスを追加するだけです。 バンドル識別子-「ru.touchin.TouchInApp」、グループ-「group.ru.touchin.TouchInApp」があります。

拡張機能のターゲットに移動し、[機能]タブに移動して、アプリグループをオンにします。 以前に入力したグループがそこに表示されます。 ダニをつけてください。

「署名の自動管理」オプションを使用すると、アプリグループは非常に簡単に構成できます。 ご覧のとおり、私はいくつかの段落に会いました。 これにより、CallKitに関する記事をApp Groupsに関する記事に変えることはできません。 ただし、開発者アカウントのプロファイルを使用する場合は、アカウントにアプリグループを追加し、アプリケーションと拡張機能のアプリIDに含める必要があります。

連絡先をファイルに書き込みます


App Groupを有効にすると、ファイルが保存されるコンテナーにアクセスできます。 これは次のように行われます。

 let container = FileManager.default .containerURL(forSecurityApplicationGroupIdentifier: "group.ru.touchin.TouchInApp") 

「Group.ru.touchin.TouchInApp」は、追加したばかりのApp Groupです。

ファイルに「contacts」という名前を付けて、そのURLを作成しましょう。

 guard let fileUrl = FileManager.default .containerURL(forSecurityApplicationGroupIdentifier: "group.ru.touchin.TouchInApp")? .appendingPathComponent("contacts") else { return } 

少し後に完全なコードが表示されますが、ここでいくつかのポイントを明確にします。

次に、数字と名前を書き込む必要があります。 次の形式で既に準備していることを前提としています。

 let numbers = ["79214203692", "79640982354", "79982434663"] let labels = ["  ", "  ", "  "] 

番号には正しい国コードを使用し、昇順で並べ替える必要があることを思い出させてください。

次に、連絡先からファイルの将来のコンテンツを作成します。

 var string = "" for (number, label) in zip(numbers, labels) { string += "\(number),\(label)\n" } 

number-nameの各ペアは、コンマで区切られて1行に書き込まれます。 改行文字で終わります。

すべてをファイルに書き込みます。

 try? string.write(to: fileUrl, atomically: true, encoding: .utf8) 

そして今、楽しい部分です。 ボウルが満杯であり、食べる時間であることを延長部に知らせる必要があります。 これを行うには、次の関数を呼び出します。

 CXCallDirectoryManager.sharedInstance.reloadExtension( withIdentifier: "ru.touchin.TouchInApp.TouchInCallExtension") 

関数パラメーターは、拡張機能のバンドルIDです。

完全なコード:

 @IBAction func addContacts(_ sender: Any) { let numbers = ["79214203692", "79640982354", "79982434663"] let labels = ["  ", "  ", "  "] writeFileForCallDirectory(numbers: numbers, labels: labels) } private func writeFileForCallDirectory(numbers: [String], labels: [String]) { guard let fileUrl = FileManager.default .containerURL(forSecurityApplicationGroupIdentifier: "group.ru.touchin.TouchInApp")? .appendingPathComponent("contacts") else { return } var string = "" for (number, label) in zip(numbers, labels) { string += "\(number),\(label)\n" } try? string.write(to: fileUrl, atomically: true, encoding: .utf8) CXCallDirectoryManager.sharedInstance.reloadExtension( withIdentifier: "ru.touchin.TouchInApp.TouchInCallExtension") } 

ファイルから連絡先を読み取る


しかし、それだけではありません。 このファイルを読み取れるように拡張機能を準備しませんでした。 一度に1行ずつファイルを読み取って、その行から番号と名前を抽出するように彼に依頼しましょう。 その後、テスト番号と同じ方法で進めます。

残念ながら、iOSにはテキストファイルを1行ずつ読み取る機能はありません。 StackOverflowのユーザーによって提案されたアプローチを使用します。 LineReaderクラスを拡張機能とともにコピーします。

CallDirectoryHandler.swiftファイルに戻って、変更を加えましょう。 まず、ファイルのURLを取得します。 これは、アプリケーションとまったく同じように行われます。 次に、ファイルへのパスでLineReader初期化します。 ファイルを1行ずつ読み取り、連絡先ごとに連絡先を追加します。

更新されたaddIdentificationPhoneNumbers関数のコード:

 private func addIdentificationPhoneNumbers(to context: CXCallDirectoryExtensionContext) throws { guard let fileUrl = FileManager.default .containerURL(forSecurityApplicationGroupIdentifier: "group.ru.touchin.TouchInApp")? .appendingPathComponent("contacts") else { return } guard let reader = LineReader(path: fileUrl.path) else { return } for line in reader { autoreleasepool { //      let line = line.trimmingCharacters(in: .whitespacesAndNewlines) //     var components = line.components(separatedBy: ",") //    Int64 guard let phone = Int64(components[0]) else { return } let name = components[1] context.addIdentificationEntry(withNextSequentialPhoneNumber: phone, label: name) } } } 

関数は最小限のリソースを使用する必要があるため、ループの繰り返しをautoreleasepoolラップします。 これにより、一時オブジェクトが解放され、使用メモリが少なくなります。

それだけです これで、 addContacts関数を呼び出した後addContacts電話はnumbers配列から数値を判別できるようになります。

GitHubリポジトリにプロジェクトの最終バージョンをダウンロードできます。

次は?


これは、問題を解決するためのオプションの1つにすぎません。 2GISのように、テキストファイルの代わりにバイナリファイルを使用して改善できます。 これにより、データをすばやく読み書きできます。 したがって、ファイルの構造を検討し、書き込みおよび読み取り用の関数を書き換える必要があります。

これがどのように機能するかを知っているとき、すべてがあなたの手にあります。

Source: https://habr.com/ru/post/J333364/


All Articles