diff --git a/.gitignore b/.gitignore index 75728c61..530536dc 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ *.xcworkspace/ project.xcworkspace/ xcuserdata/ +DerivedData/ # Podfiles Pods/ diff --git a/TelnyxRTC.podspec b/TelnyxRTC.podspec index 5b7241bc..6e951533 100644 --- a/TelnyxRTC.podspec +++ b/TelnyxRTC.podspec @@ -2,7 +2,7 @@ Pod::Spec.new do |spec| spec.name = "TelnyxRTC" - spec.version = "0.1.13" + spec.version = "0.1.15" spec.summary = "Enable Telnyx real-time communication services on iOS." spec.description = "The Telnyx iOS WebRTC Client SDK provides all the functionality you need to start making voice calls from an iPhone." spec.homepage = "https://github.com/team-telnyx/telnyx-webrtc-ios" diff --git a/TelnyxRTC.xcodeproj/project.pbxproj b/TelnyxRTC.xcodeproj/project.pbxproj index 5f8736b0..9f5ff317 100644 --- a/TelnyxRTC.xcodeproj/project.pbxproj +++ b/TelnyxRTC.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 1B0B82F8E58A096FBA0FB9CC /* Pods_TelnyxWebRTCDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 820AA51E2426DDF94E656DBF /* Pods_TelnyxWebRTCDemo.framework */; }; 3B1BE6F72AA9A467000B7962 /* TxPushIPConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B1BE6F62AA9A467000B7962 /* TxPushIPConfig.swift */; }; + 3B1F43EF2AE0B01E00A610BA /* Params.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B1F43EE2AE0B01E00A610BA /* Params.swift */; }; 3B49B7152AA9B0A20026D36D /* AttachCallMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B49B7142AA9B0A20026D36D /* AttachCallMessage.swift */; }; 3B72695D2A9396BF00D2A602 /* DisablePushMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B72695C2A9396BF00D2A602 /* DisablePushMessage.swift */; }; 568DF7EAE9BC54FA55C6C689 /* Pods_TelnyxRTC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E0D4B133B1289001948EE69E /* Pods_TelnyxRTC.framework */; }; @@ -70,6 +71,7 @@ B3E1029A25F2C16500227DCE /* ModifyMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E1029925F2C16500227DCE /* ModifyMessage.swift */; }; B3E1033225F7F94900227DCE /* incoming_call.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = B3E1033025F7F94900227DCE /* incoming_call.mp3 */; }; B3E1033325F7F94900227DCE /* ringback_tone.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = B3E1033125F7F94900227DCE /* ringback_tone.mp3 */; }; + EFE662D4D4205BA0737A8CDF /* Pods_TelnyxWebRTCDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 195824CF298256C4E12F9349 /* Pods_TelnyxWebRTCDemo.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -102,9 +104,14 @@ 2863A4F1B4A0DEA270929B3A /* Pods-TelnyxWebRTCDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TelnyxWebRTCDemo.release.xcconfig"; path = "Target Support Files/Pods-TelnyxWebRTCDemo/Pods-TelnyxWebRTCDemo.release.xcconfig"; sourceTree = ""; }; 32B652E983D4C9ED833E9501 /* Pods-TelnyxRTC.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TelnyxRTC.debug.xcconfig"; path = "Target Support Files/Pods-TelnyxRTC/Pods-TelnyxRTC.debug.xcconfig"; sourceTree = ""; }; 3B1BE6F62AA9A467000B7962 /* TxPushIPConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TxPushIPConfig.swift; sourceTree = ""; }; + 3B1F43EE2AE0B01E00A610BA /* Params.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Params.swift; sourceTree = ""; }; 3B49B7142AA9B0A20026D36D /* AttachCallMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachCallMessage.swift; sourceTree = ""; }; 3B72695C2A9396BF00D2A602 /* DisablePushMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisablePushMessage.swift; sourceTree = ""; }; - 820AA51E2426DDF94E656DBF /* Pods_TelnyxWebRTCDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TelnyxWebRTCDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4247DF5BDC3E90EC89E44712 /* Pods_TelnyxRTC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TelnyxRTC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7441B69A8427DCD0108C3C80 /* Pods-TelnyxRTC.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TelnyxRTC.release.xcconfig"; path = "Target Support Files/Pods-TelnyxRTC/Pods-TelnyxRTC.release.xcconfig"; sourceTree = ""; }; + 9001F8CFF56796278425B095 /* Pods-TelnyxRTC.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TelnyxRTC.debug.xcconfig"; path = "Target Support Files/Pods-TelnyxRTC/Pods-TelnyxRTC.debug.xcconfig"; sourceTree = ""; }; + 9A835C8C2EB192DA45FE6FDA /* Pods-TelnyxRTC-TelnyxRTCTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TelnyxRTC-TelnyxRTCTests.release.xcconfig"; path = "Target Support Files/Pods-TelnyxRTC-TelnyxRTCTests/Pods-TelnyxRTC-TelnyxRTCTests.release.xcconfig"; sourceTree = ""; }; + B2B42AFBD4625B8F66519231 /* Pods_TelnyxRTC_TelnyxRTCTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TelnyxRTC_TelnyxRTCTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B309D11125EF107F00A2AADF /* Starscream.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Starscream.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B309D1D525F020B300A2AADF /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = ""; }; B309D1DA25F020D400A2AADF /* Method.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Method.swift; sourceTree = ""; }; @@ -244,6 +251,7 @@ B39121AE25FFCE680051E076 /* TxError.swift */, B3B1D9A026542860008D28C9 /* TxPushConfig.swift */, B32AE8B226CD4F9200C7C6F4 /* TxServerConfiguration.swift */, + 3B1F43EE2AE0B01E00A610BA /* Params.swift */, ); path = Models; sourceTree = ""; @@ -682,6 +690,28 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-TelnyxWebRTCDemo/Pods-TelnyxWebRTCDemo-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + E4F7024869E64C30684DD710 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-TelnyxRTC-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -707,6 +737,7 @@ B3B1D9A126542860008D28C9 /* TxPushConfig.swift in Sources */, B309D23625F06D2100A2AADF /* TxCallInfo.swift in Sources */, B309D23B25F06DC000A2AADF /* TxCallOptions.swift in Sources */, + 3B1F43EF2AE0B01E00A610BA /* Params.swift in Sources */, B3AF249825EE7DC70062EDA9 /* SocketDelegate.swift in Sources */, B3E0B0662656ED73005E7431 /* InfoMessage.swift in Sources */, B3E1029A25F2C16500227DCE /* ModifyMessage.swift in Sources */, diff --git a/TelnyxRTC/Telnyx/Models/Params.swift b/TelnyxRTC/Telnyx/Models/Params.swift new file mode 100644 index 00000000..367ed91a --- /dev/null +++ b/TelnyxRTC/Telnyx/Models/Params.swift @@ -0,0 +1,39 @@ +// +// Params.swift +// TelnyxRTC +// +// Created by Isaac Akakpo on 19/10/2023. +// + +import Foundation + + +struct CustomHeaderData:Codable{ + let jsonrpc:String + let method:String + let params:Params +} + +struct Params: Codable { + let dialogParams: DialogParams +} + +struct DialogParams: Codable { + let custom_headers: [XHeader] +} + +struct XHeader: Codable { + let name: String + let value: String +} + +func appendCustomHeaders(customHeaders:[String:String]) -> [Any] { + var xHeaders = [Any]() + customHeaders.keys.forEach { key in + var header = [String:String]() + header["name"] = key + header["value"] = customHeaders[key] + xHeaders.append(header) + } + return xHeaders +} diff --git a/TelnyxRTC/Telnyx/TxClient.swift b/TelnyxRTC/Telnyx/TxClient.swift index 4e2a3123..ade0ed6d 100644 --- a/TelnyxRTC/Telnyx/TxClient.swift +++ b/TelnyxRTC/Telnyx/TxClient.swift @@ -143,6 +143,7 @@ public class TxClient { private var gatewayState: GatewayStates = .NOREG private var isCallFromPush: Bool = false private var currentCallId:UUID = UUID() + private var pendingAnswerHeaders = [String:String]() private var speakerOn:Bool = false func isSpeakerEnabled() -> Bool { @@ -165,7 +166,9 @@ public class TxClient { RTCAudioSession.sharedInstance().isAudioEnabled = newValue } } - + + let currentRoute = AVAudioSession.sharedInstance().currentRoute + /// Client must be registered in order to receive or place calls. public var isRegistered: Bool { get { @@ -228,14 +231,21 @@ public class TxClient { } /// To answer and control callKit active flow - public func answerFromCallkit(answerAction:CXAnswerCallAction) { + /// - Parameters: + /// - answerAction : `CXAnswerCallAction` from callKit + /// - customHeaders: (Optional) + public func answerFromCallkit(answerAction:CXAnswerCallAction,customHeaders:[String:String] = [:]) { self.answerCallAction = answerAction ///answer call if currentPushCall is not nil ///This means the client has connected and we can safelyanswer if(self.calls[currentCallId] != nil){ - self.calls[currentCallId]?.answer() + self.calls[currentCallId]?.answer(customHeaders: customHeaders) answerCallAction?.fulfill() resetPushVariables() + Logger.log.i(message: "answered from callkit") + }else{ + /// Let's Keep track od the `customHeaders` passed + pendingAnswerHeaders = customHeaders } } @@ -378,16 +388,19 @@ extension TxClient { /// - destinationNumber: The destination `SIP user address` (sip:YourSipUser@sip.telnyx.com) or `phone number`. /// - callId: The current call UUID. /// - clientState: (optional) Custom state in string format encoded in base64 + /// - customHeaders: (optional) Custom Headers to be passed over webRTC Messages, should be in the + /// format `X-key:Value` `X` is required for headers to be passed. /// - Throws: /// - sessionId is required if user is not logged in /// - socket connection error if socket is not connected /// - destination number is required to start a call. /// - Returns: The call that has been created public func newCall(callerName: String, - callerNumber: String, - destinationNumber: String, - callId: UUID, - clientState: String? = nil) throws -> Call { + callerNumber: String, + destinationNumber: String, + callId: UUID, + clientState: String? = nil, + customHeaders:[String:String] = [:]) throws -> Call { //User needs to be logged in to get a sessionId guard let sessionId = self.sessionId else { throw TxError.callFailed(reason: .sessionIdIsRequired) @@ -410,7 +423,7 @@ extension TxClient { ringtone: self.txConfig?.ringtone, ringbackTone: self.txConfig?.ringBackTone, iceServers: self.serverConfiguration.webRTCIceServers) - call.newCall(callerName: callerName, callerNumber: callerNumber, destinationNumber: destinationNumber, clientState: clientState) + call.newCall(callerName: callerName, callerNumber: callerNumber, destinationNumber: destinationNumber, clientState: clientState,customHeaders: customHeaders) currentCallId = callId self.calls[callId] = call @@ -430,7 +443,8 @@ extension TxClient { callId: UUID, remoteSdp: String, telnyxSessionId: String, - telnyxLegId: String) { + telnyxLegId: String, + customHeaders:[String:String] = [:]) { guard let sessionId = self.sessionId, let socket = self.socket else { @@ -450,7 +464,7 @@ extension TxClient { call.callInfo?.callerName = callerName call.callInfo?.callerNumber = callerNumber call.callOptions = TxCallOptions(audio: true) - + call.inviteCustomHeaders = customHeaders self.calls[callId] = call // propagate the incoming call to the App Logger.log.i(message: "TxClient:: push flow createIncomingCall \(call)") @@ -461,7 +475,7 @@ extension TxClient { self.delegate?.onPushCall(call: call) //Answer is pending from push - Answer Call if(answerCallAction != nil){ - call.answer() + call.answer(customHeaders: pendingAnswerHeaders) answerCallAction?.fulfill() resetPushVariables() } @@ -663,7 +677,7 @@ extension TxClient : SocketDelegate { let callUUIDString = params["callID"] as? String, let callUUID = UUID(uuidString: callUUIDString), let call = calls[callUUID] { - call.handleVertoMessage(message: vertoMessage,txclient: self) + call.handleVertoMessage(message: vertoMessage, dataMessage: message, txClient: self) } @@ -709,12 +723,26 @@ extension TxClient : SocketDelegate { if telnyxLegId.isEmpty { Logger.log.w(message: "TxClient:: Telnyx Leg ID unavailable on INVITE message") } + + var customHeaders = [String:String]() + if params["dialogParams"] is [String:Any] { + do { + let dataDecoded = try JSONDecoder().decode(CustomHeaderData.self, from: message.data(using: .utf8)!) + dataDecoded.params.dialogParams.custom_headers.forEach { xHeader in + customHeaders[xHeader.name] = xHeader.value + } + print("Data Decode : \(dataDecoded)") + } catch { + print("decoding error: \(error)") + } + } self.createIncomingCall(callerName: callerName, callerNumber: callerNumber, callId: uuid, remoteSdp: sdp, telnyxSessionId: telnyxSessionId, - telnyxLegId: telnyxLegId) + telnyxLegId: telnyxLegId, + customHeaders: customHeaders) } break; //Mark: to send meassage to pong diff --git a/TelnyxRTC/Telnyx/Verto/AnswerMessage.swift b/TelnyxRTC/Telnyx/Verto/AnswerMessage.swift index 85efbbb9..637db714 100644 --- a/TelnyxRTC/Telnyx/Verto/AnswerMessage.swift +++ b/TelnyxRTC/Telnyx/Verto/AnswerMessage.swift @@ -13,7 +13,8 @@ class AnswerMessage : Message { init(sessionId: String, sdp: String, callInfo: TxCallInfo, - callOptions: TxCallOptions) { + callOptions: TxCallOptions, + customHeaders:[String:String] = [:]) { var params = [String: Any]() var dialogParams = [String: Any]() @@ -26,6 +27,9 @@ class AnswerMessage : Message { params["sessionId"] = sessionId params["sdp"] = sdp + if(!customHeaders.isEmpty){ + dialogParams["custom_headers"] = appendCustomHeaders(customHeaders: customHeaders) + } params["dialogParams"] = dialogParams super.init(params, method: .ANSWER) } diff --git a/TelnyxRTC/Telnyx/Verto/InviteMessage.swift b/TelnyxRTC/Telnyx/Verto/InviteMessage.swift index 34f95882..09e21ec1 100644 --- a/TelnyxRTC/Telnyx/Verto/InviteMessage.swift +++ b/TelnyxRTC/Telnyx/Verto/InviteMessage.swift @@ -8,15 +8,17 @@ import Foundation + class InviteMessage : Message { init(sessionId: String, sdp: String, callInfo: TxCallInfo, - callOptions: TxCallOptions) { + callOptions: TxCallOptions, + customHeaders:[String:String] = [:] + ) { var params = [String: Any]() var dialogParams = [String: Any]() - dialogParams["callID"] = callInfo.callId.uuidString.lowercased() dialogParams["destination_number"] = callOptions.destinationNumber dialogParams["remote_caller_id_name"] = callOptions.remoteCallerName @@ -28,6 +30,9 @@ class InviteMessage : Message { dialogParams["attach"] = callOptions.attach dialogParams["screenShare"] = callOptions.screenShare dialogParams["userVariables"] = callOptions.userVariables + if(!customHeaders.isEmpty){ + dialogParams["custom_headers"] = appendCustomHeaders(customHeaders: customHeaders) + } if let clientState = callOptions.clientState { dialogParams["clientState"] = clientState } diff --git a/TelnyxRTC/Telnyx/WebRTC/Call.swift b/TelnyxRTC/Telnyx/WebRTC/Call.swift index 2bce440f..df7b49b4 100644 --- a/TelnyxRTC/Telnyx/WebRTC/Call.swift +++ b/TelnyxRTC/Telnyx/WebRTC/Call.swift @@ -101,6 +101,12 @@ public class Call { var remoteSdp: String? var callOptions: TxCallOptions? + /// Custum headers pased /from webrtc telnyx_rtc.INVITE Messages + public internal(set) var inviteCustomHeaders: [String:String]? + + /// Custum headers pased tfrom telnyx_rtc.ANSWER webrtcMessages + public internal(set) var answerCustomHeaders: [String:String]? + /// The Session ID of the current connection public internal(set) var sessionId: String? /// Telnyx call session ID. @@ -183,9 +189,10 @@ public class Call { /** Creates an offer to start the calling process */ - private func invite(callerName: String, callerNumber: String, destinationNumber: String, clientState: String? = nil) { + private func invite(callerName: String, callerNumber: String, destinationNumber: String, clientState: String? = nil, + customHeaders:[String:String] = [:]) { self.direction = .OUTBOUND - + self.inviteCustomHeaders = customHeaders self.callInfo?.callerName = callerName self.callInfo?.callerNumber = callerNumber self.callOptions = TxCallOptions(destinationNumber: destinationNumber, @@ -213,14 +220,14 @@ public class Call { It sets the incoming sdp as the remoteDecription. sdp: Is the remote SDP to configure in the current RTCPeerConnection */ - private func answered(sdp: String) { + private func answered(sdp: String, custumHeaders:[String:String] = [:]) { let remoteDescription = RTCSessionDescription(type: .answer, sdp: sdp) self.peer?.connection?.setRemoteDescription(remoteDescription, completionHandler: { (error) in if let error = error { Logger.log.e(message: "Call:: Error setting remote description: \(error)") return } - + self.answerCustomHeaders = custumHeaders self.updateCallState(callState: .ACTIVE) Logger.log.e(message: "Call:: connected") }) @@ -275,12 +282,13 @@ extension Call { internal func newCall(callerName: String, callerNumber: String, destinationNumber: String, - clientState: String? = nil) { + clientState: String? = nil, + customHeaders:[String:String] = [:]) { if (destinationNumber.isEmpty) { Logger.log.e(message: "Call:: Please enter a destination number.") return } - invite(callerName: callerName, callerNumber: callerNumber, destinationNumber: destinationNumber, clientState: clientState) + invite(callerName: callerName, callerNumber: callerNumber, destinationNumber: destinationNumber, clientState: clientState, customHeaders: customHeaders) } /// Hangup or reject an incoming call. @@ -298,13 +306,17 @@ extension Call { /// Starts the process to answer the incoming call. /// ### Example: /// call.answer() - public func answer() { + /// - Parameters: + /// - customHeaders: (optional) Custom Headers to be passed over webRTC Messages, should be in the + /// format `X-key:Value` `X` is required for headers to be passed. + public func answer(customHeaders:[String:String] = [:]) { self.stopRingtone() self.stopRingbackTone() //TODO: Create an error if there's no remote SDP guard let remoteSdp = self.remoteSdp else { return } + self.answerCustomHeaders = customHeaders self.peer = Peer(iceServers: self.iceServers) self.peer?.delegate = self self.incomingOffer(sdp: remoteSdp) @@ -449,7 +461,8 @@ extension Call : PeerDelegate { let inviteMessage = InviteMessage(sessionId: sessionId, sdp: sdp.sdp, callInfo: callInfo, - callOptions: callOptions) + callOptions: callOptions, + customHeaders: self.inviteCustomHeaders ?? [:]) let message = inviteMessage.encode() ?? "" self.socket?.sendMessage(message: message) @@ -457,7 +470,9 @@ extension Call : PeerDelegate { Logger.log.s(message: "Call:: Send invite >> \(message)") } else { //Build the telnyx_rtc.answer message and send it - let answerMessage = AnswerMessage(sessionId: sessionId, sdp: sdp.sdp, callInfo: callInfo, callOptions: callOptions) + let answerMessage = AnswerMessage(sessionId: sessionId, sdp: sdp.sdp, callInfo: callInfo, callOptions: callOptions, + customHeaders: self.answerCustomHeaders ?? [:] + ) let message = answerMessage.encode() ?? "" self.socket?.sendMessage(message: message) self.updateCallState(callState: .ACTIVE) @@ -472,14 +487,13 @@ extension Call : PeerDelegate { */ extension Call { - internal func handleVertoMessage(message: Message,txclient:TxClient) { + internal func handleVertoMessage(message: Message,dataMessage: String,txClient:TxClient) { switch message.method { case .BYE: //Close call self.endCall() break - case .MEDIA: self.stopRingtone() self.stopRingbackTone() @@ -509,8 +523,21 @@ extension Call { } else { Logger.log.w(message: "Call:: .ANSWER missing SDP") } + var customHeaders = [String:String]() + if params["dialogParams"] is [String:Any] { + do { + + let dataDecoded = try JSONDecoder().decode(CustomHeaderData.self, from: dataMessage.data(using: .utf8)!) + dataDecoded.params.dialogParams.custom_headers.forEach { xHeader in + customHeaders[xHeader.name] = xHeader.value + } + print("Data Decode : \(dataDecoded)") + } catch { + print("decoding error: \(error)") + } + } //retrieve the remote SDP from the ANSWER verto message and set it to the current RTCPconnection - self.answered(sdp: self.remoteSdp ?? "") + self.answered(sdp: self.remoteSdp ?? "",custumHeaders: customHeaders) } //TODO: handle error when there's no sdp break; @@ -539,9 +566,9 @@ extension Call { Logger.log.w(message: "TxClient:: SocketDelegate Default method") break } - if(txclient.isSpeakerEnabled()){ + if(txClient.isSpeakerEnabled()){ Logger.log.w(message: "Speaker Enabled") - txclient.setSpeaker() + txClient.setSpeaker() } } } diff --git a/TelnyxWebRTCDemo/Extensions/AppDelegateCallKitExtension.swift b/TelnyxWebRTCDemo/Extensions/AppDelegateCallKitExtension.swift index 0d62883e..57cc92ab 100644 --- a/TelnyxWebRTCDemo/Extensions/AppDelegateCallKitExtension.swift +++ b/TelnyxWebRTCDemo/Extensions/AppDelegateCallKitExtension.swift @@ -137,7 +137,7 @@ extension AppDelegate : CXProviderDelegate { self.callKitUUID = nil } } - // MARK: - CXProviderDelegate - + // MARK: - CXProviderDelegate - func provider(_ provider: CXProvider, perform action: CXStartCallAction) { print("AppDelegate:: START call action: callKitUUID [\(String(describing: self.callKitUUID))] action [\(action.callUUID)]") self.callKitUUID = action.callUUID @@ -156,7 +156,7 @@ extension AppDelegate : CXProviderDelegate { func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { print("AppDelegate:: ANSWER call action: callKitUUID [\(String(describing: self.callKitUUID))] action [\(action.callUUID)]") - self.telnyxClient?.answerFromCallkit(answerAction: action) + self.telnyxClient?.answerFromCallkit(answerAction: action, customHeaders: ["X-test-answer":"ios-test"]) } func provider(_ provider: CXProvider, perform action: CXEndCallAction) { diff --git a/TelnyxWebRTCDemo/Extensions/AppDelegateTelnyxVoIPExtension.swift b/TelnyxWebRTCDemo/Extensions/AppDelegateTelnyxVoIPExtension.swift index fd189a33..e4b65828 100644 --- a/TelnyxWebRTCDemo/Extensions/AppDelegateTelnyxVoIPExtension.swift +++ b/TelnyxWebRTCDemo/Extensions/AppDelegateTelnyxVoIPExtension.swift @@ -57,6 +57,8 @@ extension AppDelegate: TxClientDelegate { } self.callKitUUID = call.callInfo?.callId self.currentCall = call //Update the current call with the incoming call + let headers = call.inviteCustomHeaders + print("\n Custom Headers onIncomingCall: \(String(describing: headers)) \n") self.newIncomingCall(from: call.callInfo?.callerName ?? "Unknown", uuid: callId) self.voipDelegate?.onIncomingCall(call: call) } @@ -64,6 +66,8 @@ extension AppDelegate: TxClientDelegate { func onPushCall(call: Call) { print("AppDelegate:: TxClientDelegate onPushCall() \(call)") self.currentCall = call //Update the current call with the incoming call + let headers = call.inviteCustomHeaders + print("Custom Headers onPushCall: \(headers as AnyObject)") } func onRemoteCallEnded(callId: UUID) { @@ -80,6 +84,12 @@ extension AppDelegate: TxClientDelegate { print("AppDelegate:: TxClientDelegate onCallStateUpdated() callKitUUID [\(String(describing: self.callKitUUID))] callId [\(callId)]") self.voipDelegate?.onCallStateUpdated(callState: callState, callId: callId) + if(callState == .ACTIVE){ + // check if custom headers was passed for answered message + let headers = self.currentCall?.answerCustomHeaders + print("Custom Headers: \(headers as AnyObject)") + } + if callState == .DONE { if let currentCallId = self.currentCall?.callInfo?.callId, currentCallId == callId { diff --git a/TelnyxWebRTCDemo/Extensions/ViewControllerVoIPExtension.swift b/TelnyxWebRTCDemo/Extensions/ViewControllerVoIPExtension.swift index a31cfb6e..dc4ef8d6 100644 --- a/TelnyxWebRTCDemo/Extensions/ViewControllerVoIPExtension.swift +++ b/TelnyxWebRTCDemo/Extensions/ViewControllerVoIPExtension.swift @@ -117,6 +117,13 @@ extension ViewController : VoIPDelegate { } } + func setCurrentAudioOutput(){ + if(self.isSpeakerActive){ + self.telnyxClient?.setSpeaker() + } + } + + func executeCall(callUUID: UUID, completionHandler: @escaping (Call?) -> Void) { do { guard let callerName = self.settingsView.callerIdNameLabel.text, @@ -125,11 +132,13 @@ extension ViewController : VoIPDelegate { print("ERROR: executeCall can't be performed. Check callerName - callerNumber and destinationNumber") return } + let headers = ["X-test1":"ios-test1", + "X-test2":"ios-test2"] let call = try telnyxClient?.newCall(callerName: callerName, - callerNumber: callerNumber, - destinationNumber: destinationNumber, - callId: callUUID) + callerNumber: callerNumber, + destinationNumber: destinationNumber, + callId: callUUID,customHeaders: headers) completionHandler(call) } catch let error { print("ViewController:: executeCall Error \(error)")