// // CallExample.swift // CallTutorial // // Created by QuentinArguillere on 31/07/2020. // Copyright © 2020 BelledonneCommunications. All rights reserved. // import linphonesw import AVFoundation import Combine class CallKitExampleContext : ObservableObject { @Published var mCore: Core? = nil @Published var factory = Factory.Instance @Published var coreVersion: String = Core.getVersion @Published var username : String = "quentindev" @Published var passwd : String = "dev" @Published var domain : String = "sip.linphone.org" @Published var loggedIn: Bool = false @Published var transportType : String = "TLS" @Published var callMsg : String = "" @Published var isCallIncoming : Bool = false @Published var isCallRunning : Bool = false @Published var remoteAddress : String = "Nobody yet" @Published var isSpeakerEnabled : Bool = false @Published var isMicrophoneEnabled : Bool = false var cancellables = Set() var icancellables = Set() /* Async */ let linphoneAsyncHelper = LinphoneAsyncHelper() /*------------ Callkit tutorial related variables ---------------*/ let incomingCallName = "Incoming call example" var mCall : Call? var mProviderDelegate : CallKitProviderDelegate! var mCallAlreadyStopped : Bool = false; func addRegistrationStateCallBack(core:Core) { core.createAccountRegistrationStateChangedPublisher() .postOnMainQueue { result in NSLog("New registration state is \(result.state) for user id \( String(describing: result.account.params?.identityAddress?.asString()))\n") if (result.state == .Ok) { self.loggedIn = true // Since core has "Push Enabled", the reception and setting of the push notification token is done automatically // It should have been set and used when we log in, you can check here or in the liblinphone logs NSLog("Account registered Push voip token: \(result.account.params?.pushNotificationConfig?.voipToken)") } else if (result.state == .Cleared) { self.loggedIn = false } } .postOnCoreQueue{ result in // optional something on core queue if needed } } func addCallStateChangedCallBack(core:Core) { core.createOnCallStateChangedPublisher() .postOnMainQueue { result in self.callMsg = result.message if (result.state == .PushIncomingReceived){ // We're being called by someone (and app is in background) self.mCall = result.call self.mProviderDelegate.incomingCall() self.isCallIncoming = true self.callMsg = result.message } else if (result.state == .IncomingReceived) { // If app is in foreground, it's likely that we will receive the SIP invite before the Push notification if (!self.isCallIncoming) { self.mCall = result.call self.mProviderDelegate.incomingCall() self.isCallIncoming = true self.callMsg = result.message } self.remoteAddress = result.call.remoteAddress!.asStringUriOnly() } else if (result.state == .Connected) { self.isCallIncoming = false self.isCallRunning = true } else if (result.state == .Released || result.state == .End || result.state == .Error) { // Call has been terminated by any side // Report to CallKit that the call is over, if the terminate action was initiated by other end of the call if (self.isCallRunning) { self.mProviderDelegate.stopCall() } self.remoteAddress = "Nobody yet" } } .postOnCoreQueue{ result in // optional something on core queue if needed } } init() { LoggingService.Instance.logLevel = LogLevel.Debug factory = Factory.Instance $factory.receive(on: coreQueue).sink { factory in let configDir = factory.getConfigDir(context: nil) do { let core = try factory.createCore(configPath: "\(configDir)/MyConfig", factoryConfigPath: "", systemContext: nil) core.callkitEnabled = true core.pushNotificationEnabled = true core.autoIterateEnabled = false self.addRegistrationStateCallBack(core: core) self.addCallStateChangedCallBack(core: core) try? core.start() LinphoneAsyncHelper.postOnMainQueue { self.mCore = core // @Publisher assignment can be done on main queue only } } catch { NSLog("failed creating core \(error)") } }.store(in: &cancellables) Timer.scheduledTimer(withTimeInterval: 0.02, repeats: true) { _ in self.icancellables.removeAll() self.$mCore.receive(on: coreQueue).sink { core in core?.iterate() }.store(in: &self.icancellables) } mProviderDelegate = CallKitProviderDelegate(context: self) } func login() { $factory.receive(on: coreQueue).sink { factory in do { var transport : TransportType if (self.transportType == "TLS") { transport = TransportType.Tls } else if (self.transportType == "TCP") { transport = TransportType.Tcp } else { transport = TransportType.Udp } let authInfo = try Factory.Instance.createAuthInfo(username: self.username, userid: "", passwd: self.passwd, ha1: "", realm: "", domain: self.domain) let accountParams = try self.mCore?.createAccountParams() let identity = try Factory.Instance.createAddress(addr: String("sip:" + self.username + "@" + self.domain)) try! accountParams?.setIdentityaddress(newValue: identity) let address = try Factory.Instance.createAddress(addr: String("sip:" + self.domain)) try address.setTransport(newValue: transport) try accountParams?.setServeraddress(newValue: address) accountParams?.registerEnabled = true // Enable push notifications on this account accountParams?.pushNotificationAllowed = true // We're in a sandbox application, so we must set the provider to "apns.dev" since it will be "apns" by default, which is used only for production apps accountParams?.pushNotificationConfig?.provider = "apns.dev" let account = try self.mCore?.createAccount(params: accountParams!) self.mCore?.addAuthInfo(info: authInfo) try self.mCore?.addAccount(account: account!) self.mCore?.defaultAccount = account } catch { NSLog(error.localizedDescription) } }.store(in: &cancellables) } func unregister() { $mCore.receive(on: coreQueue).sink { core in guard let account = core?.defaultAccount else {return} let params = account.params let clonedParams = params?.clone() clonedParams?.registerEnabled = false account.params = clonedParams }.store(in: &cancellables) } func delete() { $mCore.receive(on: coreQueue).sink { core in guard let account = core?.defaultAccount else {return} self.mCore?.removeAccount(account: account) self.mCore?.clearAccounts() self.mCore?.clearAllAuthInfo() }.store(in: &cancellables) } }