[Swift]CallKitとPushKitを使い通話の発着信を実装する


本記事はCallKit、PushKitを用いた通話機能実装の全体像を掴んでいただくことを目的としています。

自分自身も今回始めて実装しました。私と同じ境遇の方に少しでもお役に立てたら幸いです。
また何か間違いなどございましたら教えていただけると助かります。

初めに結論から申し上げると、これらを実装することで新たなリモート通知を使い、いつもよく見るiPhoneの着信画面を表示できるようになります。

詳しく紹介していきます！

## CallKitってなんですか？
https://developer.apple.com/documentation/callkit

ドキュメントにはこのように書いてありました。

>CallKitは電話アプリと同じインターフェイスを表示し、アプリにネイティブなルックアンドフィールを提供します。CallKitは、サイレントなどのシステムレベルの動作にも適切に応答します。


**要するに、システムの状況に関わらず、アプリにネイティブな通話画面のデザインや操作感を提供してくれるということですね！:sunny:**

そのCallKItのトリガーとなるのがVoipPush、すなわちPushKitです。

## VoipPushってなんですか？

VoipPushは通話専用のPush通知であり、通常のPush通知とは異なります。

https://developer.apple.com/documentation/pushkit/pkpushtype/1614481-voip

>アプリがVoIP通知をCallKitに繰り返し報告しない場合、システムはVoIPプッシュ通知のためにアプリの起動を停止します

:zap: 注意点としてVoipPushはあくまで通話専用のPush通知ということです。:zap:
上記ドキュメント記載のようにVoip通知をCallKitに伝えないとアプリが終了します。


## 実装の全体像を掴む

初めて実装するときは全体像を掴んで道筋を立てて実装するの楽だと思います。
なのでここで全体像を掴んでもらえたら嬉しいです。

CallKitの説明で記載したとおりCallKitのトリガーとなるのはVoipPushです。
**VoipPushを受け取りCallKitで通話を管理するというイメージです。**

よって実装順番としては私は下記の様にしました。
あくまでも私の実装順なのでこれが正解というわけではないです。

**1.  VoipPush専用の証明書を作成
2.  AWSや、サーバでVoipを送信する処理を実装　(割愛させてもらいます)
3.  クライアントでVoipを受け取る準備をする
4. CallKit, PushKitをクライアントで実装
5.  着信画面を表示**


##  VoipPush専用の証明書を作成

通常の Push Notification などの場合と同様にApple Developer 発行できます。
その際にVoIP Services Certificateを選択してください。


## クライアントでVoipを受け取る準備をする

![49383c531c059f1fcfbfaa2bd53a52a1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1624020/64144806-6ea5-6776-3e37-7c783de4a241.png)

**Voice over IP** にチェックを入れる
:zap:これにチェックを入れないとCallKitのUIを表示できなくなります:zap:

## CallKit, PushKitをクライアントで実装

### CallKitを初期化する

```swift
    fileprivate let controller = CXCallController()
    private let provider = CXProvider(configuration: CallCenter.providerConfiguration)

    private static var providerConfiguration: CXProviderConfiguration {
        let appName = "TestApp"
        let providerConfiguration = CXProviderConfiguration(localizedName: appName)
        providerConfiguration.supportsVideo = true
        providerConfiguration.maximumCallsPerCallGroup = 1
        providerConfiguration.maximumCallGroups = 1
        providerConfiguration.supportedHandleTypes = [.phoneNumber]

        return providerConfiguration
    }

```

**CXCallController**は通話の操作や、管理を行うインターフェイスを提供しているクラスです。


https://developer.apple.com/documentation/callkit/cxcallcontroller

**CXProviderConfiguration**にはCallKitのUIを制御するためのプロパティーがあります。
具体的にはCallKitの表示UI内のAppの名前を設定したり、アイコンを挿入したり、着信音を変更するといったUIの制御を行うことができます。

https://developer.apple.com/documentation/callkit/cxproviderconfiguration

## 着信画面を表示する(CallKitに着信を知らせる)

```swift
    func showIncomingCall(of session: String) {
        let callUpdate = CXCallUpdate()
        callUpdate.remoteHandle = CXHandle(type: .phoneNumber, value: session)
        callUpdate.localizedCallerName = session
        callUpdate.hasVideo = true
        callUpdate.supportsDTMF = false
        
        let uuid = pairedUUID(of: session)
        
        provider.reportNewIncomingCall(with: uuid, update: callUpdate, completion: { error in
            if let error = error {
                print("reportNewIncomingCall error: \(error.localizedDescription)")
            }
        })
    }

```

CXProviderの**reportNewIncomingCall**が通話発信のAPIです。
通話発信時、すなわちVoipPushが届いた際にこの関数を呼ぶことで、CallKitに通話着信を通知します。

引数のStringには通話毎にユニークな uuid を渡します。



:zap:**この他にも、通話を発信したとき、ミュートにしたとき、通話が切断したときなども同様にCallKitに通知を行う必要があります。**

# まとめ

冒頭にもあったように今回はCallKit、PushKitでの通話実装の全体像を掴むことを目的として書きました。
また自分自身の実装メモとして残して置きたいという思いもありました。

本記事がこれからCallKit、PushKitの着信実装する方の役にたてば光栄です。