CallKit との統合
このドキュメントでは、CallKit と iOS アプリケーションを統合する方法について説明します。
前提条件
- アクティブなサブスクリプションが含まれる Azure アカウント。 無料でアカウントを作成できます。
- デプロイ済みの Communication Services リソース。 Communication Services リソースを作成します。
- 通話クライアントを有効にするためのユーザー アクセス トークン。 詳細については、アクセス トークンの作成と管理に関する記事を参照してください。
- 省略可能: クイックスタートを完了して、アプリケーションに音声通話を追加します
CallKit 統合 (SDK 内)
Azure Communication Services iOS SDK に CallKit を統合すると、CallKit との対話が自動的に処理されます。 ミュートとミュート解除、保留と再開などの呼び出し操作を実行するために必要なのは、Azure Communication Services SDK で API を呼び出すことだけです。
CallKitOptions を使用して呼び出しエージェントを初期化する
CallKitOptions
の構成されたインスタンスを使用すると、CallKit
の処理を使用して CallAgent
を作成できます。
let options = CallAgentOptions()
let callKitOptions = CallKitOptions(with: createProviderConfig())
options.callKitOptions = callKitOptions
// Configure the properties of `CallKitOptions` instance here
self.callClient!.createCallAgent(userCredential: userCredential,
options: options,
completionHandler: { (callAgent, error) in
// Initialization
})
発信通話の通話受信者情報を指定する
まず、発信呼び出し用の StartCallOptions()
のインスタンスを作成するか、グループ呼び出し用に JoinCallOptions()
する必要があります:
let options = StartCallOptions()
または
let options = JoinCallOptions()
次に、CallKitRemoteInfo
のインスタンスを作成します
options.callKitRemoteInfo = CallKitRemoteInfo()
callKitRemoteInfo.displayNameForCallKit
の値を割り当てて、通話受信者の表示名をカスタマイズし、CXHandle
値を構成します。displayNameForCallKit
で指定されたこの値は、最後にダイヤルされた通話ログに正確に表示されます。 最後にダイヤルされた通話ログに記録されます。
options.callKitRemoteInfo.displayNameForCallKit = "DISPLAY_NAME"
cxHandle
値を割り当てると、ユーザーがその連絡先にコール バックしたときにアプリケーションが受け取る値になります
options.callKitRemoteInfo.cxHandle = CXHandle(type: .generic, value: "VALUE_TO_CXHANDLE")
着信通話の通話受信者情報を指定する
まず、次の CallKitOptions
インスタンスを作成する必要があります:
let callKitOptions = CallKitOptions(with: createProviderConfig())
CallKitOptions
インスタンスのプロパティを構成します:
変数 provideRemoteInfo
に渡されるブロックは、着信呼び出しを受信したときに SDK によって呼び出され、CallKit に渡す必要がある着信呼び出し元の表示名を取得する必要があります。
callKitOptions.provideRemoteInfo = self.provideCallKitRemoteInfo
func provideCallKitRemoteInfo(callerInfo: CallerInfo) -> CallKitRemoteInfo
{
let callKitRemoteInfo = CallKitRemoteInfo()
callKitRemoteInfo.displayName = "CALL_TO_PHONENUMBER_BY_APP"
callKitRemoteInfo.cxHandle = CXHandle(type: .generic, value: "VALUE_TO_CXHANDLE")
return callKitRemoteInfo
}
オーディオ セッションを構成する
オーディオ セッションの構成は、着信通話を発信または受け入れる前、および保留にされた後に通話を再開する前に呼び出されます。
callKitOptions.configureAudioSession = self.configureAudioSession
public func configureAudioSession() -> Error? {
let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
var configError: Error?
do {
try audioSession.setCategory(.playAndRecord)
} catch {
configError = error
}
return configError
}
注: Contoso が既にオーディオ セッションを構成している場合は、nil
を提供せず、ブロック内で nil
エラーを返します
callKitOptions.configureAudioSession = self.configureAudioSession
public func configureAudioSession() -> Error? {
return nil
}
configureAudioSession
に nil
が指定されている場合、SDK は SDK の既定の実装を呼び出します。
受信プッシュ通知ペイロードを処理する
アプリが受信プッシュ通知ペイロードを受信したら、それを処理するために handlePush
を呼び出す必要があります。 Azure Communication Services Calling SDK から、IncomingCall
イベントが発生します。
public func handlePushNotification(_ pushPayload: PKPushPayload)
{
let callNotification = PushNotificationInfo.fromDictionary(pushPayload.dictionaryPayload)
if let agent = self.callAgent {
agent.handlePush(notification: callNotification) { (error) in }
}
}
// Event raised by the SDK
public func callAgent(_ callAgent: CallAgent, didRecieveIncomingCall incomingcall: IncomingCall) {
}
reportIncomingCall
を使用して、アプリが閉じられたり、それ以外の場合にプッシュ通知を処理できます。
if let agent = self.callAgent {
/* App is not in a killed state */
agent.handlePush(notification: callNotification) { (error) in }
} else {
/* App is in a killed state */
CallClient.reportIncomingCall(with: callNotification, callKitOptions: callKitOptions) { (error) in
if (error == nil) {
DispatchQueue.global().async {
self.callClient = CallClient()
let options = CallAgentOptions()
let callKitOptions = CallKitOptions(with: createProviderConfig())
callKitOptions.provideRemoteInfo = self.provideCallKitRemoteInfo
callKitOptions.configureAudioSession = self.configureAudioSession
options.callKitOptions = callKitOptions
self.callClient!.createCallAgent(userCredential: userCredential,
options: options,
completionHandler: { (callAgent, error) in
if (error == nil) {
self.callAgent = callAgent
self.callAgent!.handlePush(notification: callNotification) { (error) in }
}
})
}
} else {
os_log("SDK couldn't handle push notification", log:self.log)
}
}
}
CallKit 統合 (アプリ内)
アプリ内に CallKit を統合し、SDK で CallKit 実装を使用しない場合は、こちらのクイックスタート サンプルを参照してください。 しかし、重要なことの 1 つは、適切なタイミングでオーディオを開始することです。 次のように
let outgoingAudioOptions = OutgoingAudioOptions()
outgoingAudioOptions.muted = true
let incomingAudioOptions = IncomingAudioOptions()
incomingAudioOptions.muted = true
var copyAcceptCallOptions = AcceptCallOptions()
copyStartCallOptions.outgoingAudioOptions = outgoingAudioOptions
copyStartCallOptions.incomingAudioOptions = incomingAudioOptions
callAgent.startCall(participants: participants,
options: copyStartCallOptions,
completionHandler: completionBlock)
スピーカーとマイクをミュートすると、CallKit が CXProviderDelegate
で didActivateAudioSession
を呼び出すまで、物理オーディオ デバイスが使用されないようにします。 そうしないと、呼び出しがドロップされたり、音声が機能しない可能性があります。
didActivateAudioSession
は、オーディオ ストリームを開始する必要があるタイミングです。
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
Task {
guard let activeCall = await self.callKitHelper.getActiveCall() else {
print("No active calls found when activating audio session !!")
return
}
try await startAudio(call: activeCall)
}
}
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
Task {
guard let activeCall = await self.callKitHelper.getActiveCall() else {
print("No active calls found when deactivating audio session !!")
return
}
try await stopAudio(call: activeCall)
}
}
private func stopAudio(call: Call) async throws {
try await self.callKitHelper.muteCall(callId: call.id, isMuted: true)
try await call.stopAudio(stream: call.activeOutgoingAudioStream)
try await call.stopAudio(stream: call.activeIncomingAudioStream)
try await call.muteIncomingAudio()
}
private func startAudio(call: Call) async throws {
try await call.startAudio(stream: LocalOutgoingAudioStream())
try await self.callKitHelper.muteCall(callId: call.id, isMuted: false)
try await call.startAudio(stream: RemoteIncomingAudioStream())
try await call.unmuteIncomingAudio()
}
CallKit が didActivateAudioSession
を呼び出さない場合は、音声を停止する前に発信オーディオをミュートすることも重要です。 その後、ユーザーはマイクのミュートを手動で解除できます。
Note
場合によっては、アプリが昇格されたオーディオアクセス許可を持っていても、CallKit は didActivateAudioSession
を呼び出しません。その場合、コールバックが受信されるまでオーディオはミュートされたままです。 また、UI はスピーカーとマイクの状態を反映する必要があります。 通話中のリモート参加者は、ユーザーも音声をミュートしていることがわかります。 このような場合、ユーザーは手動でミュートを解除する必要があります。