Files
linphone-tutorials/ios/swift/4-CallKitTutorial/CallKitTutorial/CallKitTutorial.swift

188 lines
6.6 KiB
Swift

//
// 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<AnyCancellable>()
var icancellables = Set<AnyCancellable>()
/* 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)
}
}