與 CallKit 整合
在本文件中,我們將逐步解說如何整合 CallKit 與您的 iOS 應用程式。
必要條件
- 具有有效訂用帳戶的 Azure 帳戶。 免費建立帳戶。
- 已部署通訊服務資源。 建立通訊服務資源。
- 用來啟用呼叫用戶端的使用者存取權杖。 如需詳細資訊,請參閱建立和管理存取權杖。
- 選擇性:完成快速入門以將語音通話新增至您的應用程式
CallKit 整合 (在 SDK 内)
Azure 通訊服務 iOS SDK 中的 CallKit 整合會為我們處理與 CallKit 的互動。 若要執行任何通話作業,例如靜音/取消靜音、保留/繼續,我們只需要在 Azure 通訊服務 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
執行個體的屬性:
當我們收到來電時,SDK 會通話傳遞至變數 provideRemoteInfo
的區塊,而我們需要取得傳入通話者的顯示名稱,我們需要將它傳遞至 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
}
如果 nil
提供給 configureAudioSession
SDK,SDK 會通話 SDK 中的預設實作。
處理傳入推播通知承載
當應用程式收到傳入推播通知承載時,我們需要呼叫 handlePush
來進行處理。 Azure 通訊服務通話 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 實作,請參閱這裡的快速入門範例。 但要處理的重要事項之一是在正確的時間啟動音訊。 如下所示
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
時,請務必在停止音訊之前將傳出音訊設為靜音。 然後,使用者可以手動取消靜音麥克風。
注意
在某些情況下,CallKit 不會通話 didActivateAudioSession
即使應用程式具有較高的音訊權限,在此情況下,音訊會保持靜音,直到收到回電為止。 UI 必須反映喇叭和麥克風的狀態。 通話中的遠端參與者也會看到使用者已靜音音訊。 在這些情況下,使用者必須手動取消靜音。