AzureCommunicationUI/sdk/AzureCommunicationUICalling/Sources/Service/Calling/AzureCommunicationCalling/CallingSDKInitializer.swift (247 lines of code) (raw):
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
import AzureCommunicationCalling
import Foundation
internal class CallingSDKInitializer: NSObject {
// native calling SDK keeps single reference of call agent
// this is to ensure that we don't create multiple call agents
// destroying call agent is time consuming and we don't want to do it
private var callClient: CallClient?
private var callAgent: CallAgent?
private var incomingCall: AzureCommunicationCalling.IncomingCall?
private var displayName: String?
private var callKitOptions: AzureCommunicationUICalling.CallKitOptions?
private var disableInternalPushForIncomingCall = false
private var tags: [String]
private var credential: CommunicationTokenCredential
private var logger: Logger
private var events: CallComposite.Events
private var onCallAdded: (String) -> Void
init(tags: [String],
credential: CommunicationTokenCredential,
callKitOptions: CallKitOptions?,
displayName: String? = nil,
disableInternalPushForIncomingCall: Bool,
logger: Logger,
events: CallComposite.Events,
onCallAdded: @escaping (String) -> Void) {
self.logger = logger
self.tags = tags
self.credential = credential
self.callKitOptions = callKitOptions
self.displayName = displayName
self.disableInternalPushForIncomingCall = disableInternalPushForIncomingCall
self.events = events
self.onCallAdded = onCallAdded
}
func getIncomingCall() -> AzureCommunicationCalling.IncomingCall? {
return incomingCall
}
func setupCallClient() -> CallClient {
if self.callClient == nil {
self.callClient = makeCallClient()
}
return self.callClient!
}
func setupCallAgent() async throws -> CallAgent {
if let existingCallAgent = self.callAgent {
logger.debug("Reusing call agent")
return existingCallAgent
}
let callClient = setupCallClient()
let options = CallAgentOptions()
options.disableInternalPushForIncomingCall = disableInternalPushForIncomingCall
if let providerConfig = callKitOptions?.providerConfig {
let sdkCallKitOptions = configureSDKCallKitOptions(with: providerConfig)
options.callKitOptions = sdkCallKitOptions
}
if let displayName = displayName {
options.displayName = displayName
}
do {
let callAgent = try await callClient.createCallAgent(
userCredential: credential,
options: options
)
self.callAgent = callAgent
callAgent.delegate = self
return callAgent
} catch {
logger.error("It was not possible to create a call agent.")
throw error
}
}
func registerPushNotification(deviceRegistrationToken: Data,
completionHandler: ((Result<Void, Error>) -> Void)? = nil) {
Task {
do {
let callAgent = try await setupCallAgent()
try await callAgent.registerPushNotifications(
deviceToken: deviceRegistrationToken)
completionHandler?(.success(()))
} catch {
logger.error("Failed to registerPushNotification")
completionHandler?(.failure(error))
}
}
}
func unregisterPushNotifications(completionHandler: ((Result<Void, Error>) -> Void)? = nil) {
Task {
do {
let callAgent = try await setupCallAgent()
try await callAgent.unregisterPushNotification()
completionHandler?(.success(()))
} catch {
logger.error("Failed to unregisterPushNotification")
completionHandler?(.failure(error))
}
}
}
static func reportIncomingCall(pushNotification: PushNotification,
callKitOptions: CallKitOptions,
completionHandler: ((Result<Void, Error>) -> Void)? = nil) {
let sdkCallKitOptions = AzureCommunicationCalling.CallKitOptions(with: callKitOptions.providerConfig)
sdkCallKitOptions.isCallHoldSupported = callKitOptions.isCallHoldSupported
sdkCallKitOptions.configureAudioSession = callKitOptions.configureAudioSession
if let provideRemoteInfo = callKitOptions.provideRemoteInfo {
sdkCallKitOptions.provideRemoteInfo = { (callerInfo: AzureCommunicationCalling.CallerInfo)
-> AzureCommunicationCalling.CallKitRemoteInfo in
let info = provideRemoteInfo(
Caller(displayName: callerInfo.displayName,
identifier: callerInfo.identifier))
let callKitRemoteInfo = AzureCommunicationCalling.CallKitRemoteInfo()
callKitRemoteInfo.displayName = info.displayName
callKitRemoteInfo.handle = info.handle
return callKitRemoteInfo
}
}
let pushNotificationInfo = PushNotificationInfo.fromDictionary(pushNotification.data)
CallClient.reportIncomingCall(
with: pushNotificationInfo,
callKitOptions: sdkCallKitOptions
) { error in
if let error = error {
completionHandler?(.failure(error))
} else {
completionHandler?(.success(()))
}
}
}
func handlePushNotification(pushNotification: PushNotification,
completionHandler: ((Result<Void, Error>) -> Void)? = nil) {
let pushNotificationInfo = PushNotificationInfo.fromDictionary(pushNotification.data)
if let providerConfig = callKitOptions?.providerConfig {
let sdkCallKitOptions = configureSDKCallKitOptions(with: providerConfig)
CallClient.reportIncomingCall(with: pushNotificationInfo, callKitOptions: sdkCallKitOptions) { error in
if let error = error {
completionHandler?(.failure(error))
} else {
self.handlePushNotificationAsync(pushNotificationInfo: pushNotificationInfo,
completionHandler: completionHandler)
}
}
} else {
handlePushNotificationAsync(pushNotificationInfo: pushNotificationInfo,
completionHandler: completionHandler)
}
}
private func configureSDKCallKitOptions(with providerConfig: CXProviderConfiguration) ->
AzureCommunicationCalling.CallKitOptions {
let sdkCallKitOptions = AzureCommunicationCalling.CallKitOptions(with: providerConfig)
sdkCallKitOptions.isCallHoldSupported = callKitOptions?.isCallHoldSupported ?? false
sdkCallKitOptions.configureAudioSession = callKitOptions?.configureAudioSession
if let provideRemoteInfo = callKitOptions?.provideRemoteInfo {
sdkCallKitOptions.provideRemoteInfo = { (callerInfo: AzureCommunicationCalling.CallerInfo) in
let info = provideRemoteInfo(Caller(displayName: callerInfo.displayName,
identifier: callerInfo.identifier))
let callKitRemoteInfo = AzureCommunicationCalling.CallKitRemoteInfo()
callKitRemoteInfo.displayName = info.displayName
callKitRemoteInfo.handle = info.handle
return callKitRemoteInfo
}
}
return sdkCallKitOptions
}
private func handlePushNotificationAsync(pushNotificationInfo: PushNotificationInfo,
completionHandler: ((Result<Void, Error>) -> Void)?) {
Task {
do {
let callAgent = try await self.setupCallAgent()
try await callAgent.handlePush(notification: pushNotificationInfo)
completionHandler?(.success(()))
} catch {
self.logger.error("Failed to handle push notification")
completionHandler?(.failure(error))
}
}
}
func reject(incomingCallId: String, completionHandler: ((Result<Void, Error>) -> Void)? = nil) {
if incomingCall == nil ||
incomingCall?.id != incomingCallId {
completionHandler?(.failure(IncomingCallError.callIdNotFound))
}
incomingCall?.reject { error in
if let error = error {
completionHandler?(.failure(error))
} else {
self.incomingCall?.delegate = nil
self.incomingCall = nil
completionHandler?(.success(()))
}
}
}
func dispose() {
incomingCall?.delegate = nil
incomingCall = nil
self.callAgent?.delegate = nil
self.callAgent?.dispose()
self.callAgent = nil
self.callClient = nil
}
func onIncomingCallAccpeted() {
incomingCall?.delegate = nil
incomingCall = nil
}
private func makeCallClient() -> CallClient {
let clientOptions = CallClientOptions()
let appendingTag = tags
let diagnostics = clientOptions.diagnostics ?? CallDiagnosticsOptions()
diagnostics.tags.append(contentsOf: appendingTag)
clientOptions.diagnostics = diagnostics
return CallClient(options: clientOptions)
}
}
extension CallingSDKInitializer: CallAgentDelegate {
public func callAgent(_ callAgent: CallAgent, didUpdateCalls args: CallsUpdatedEventArgs) {
if !args.addedCalls.isEmpty {
if let call = args.addedCalls.first {
let callId = call.id
self.logger.debug("on calls update received \(callId)")
self.onCallAdded(callId)
}
}
}
public func callAgent(_ callAgent: CallAgent,
didRecieveIncomingCall incomingCall: AzureCommunicationCalling.IncomingCall) {
self.incomingCall = incomingCall
incomingCall.delegate = self
self.logger.debug("incoming all received \(incomingCall.id)")
let incomingCallInfo = IncomingCall(
callId: incomingCall.id,
callerDisplayName: incomingCall.callerInfo.displayName,
callerIdentifier: incomingCall.callerInfo.identifier)
guard let onIncomingCallEventHandler = events.onIncomingCall else {
return
}
onIncomingCallEventHandler(incomingCallInfo)
}
}
extension CallingSDKInitializer: IncomingCallDelegate {
func incomingCall(_ incomingCall: AzureCommunicationCalling.IncomingCall, didEnd args: PropertyChangedEventArgs) {
guard let onIncomingCallCancelled = events.onIncomingCallCancelled,
let callEndReason = incomingCall.callEndReason else {
return
}
let callCancelled = IncomingCallCancelled(
callId: incomingCall.id,
code: Int(callEndReason.code),
subCode: Int(callEndReason.subcode)
)
onIncomingCallCancelled(callCancelled)
}
}