Suggestions from CD for Async Call backs and Core object using @Published
This commit is contained in:
parent
8ee3e46dbc
commit
3f0264edef
@ -64,30 +64,38 @@ extension CallKitProviderDelegate: CXProviderDelegate {
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
|
||||
NSLog("Callkit CXEndCallAction -- Is main thread ? \(Thread.isMainThread ? "yes" : "no") ")
|
||||
do {
|
||||
if (tutorialContext.mCall?.state != .End && tutorialContext.mCall?.state != .Released) {
|
||||
try tutorialContext.mCall?.terminate()
|
||||
}
|
||||
} catch { NSLog(error.localizedDescription) }
|
||||
tutorialContext.$mCore.receive(on: coreQueue).sink { core in
|
||||
do {
|
||||
if (self.tutorialContext.mCall?.state != .End && self.tutorialContext.mCall?.state != .Released) {
|
||||
try self.tutorialContext.mCall?.terminate()
|
||||
}
|
||||
LinphoneAsyncHelper.postOnMainQueue {
|
||||
self.tutorialContext.isCallRunning = false
|
||||
self.tutorialContext.isCallIncoming = false
|
||||
}
|
||||
} catch { NSLog(error.localizedDescription) }
|
||||
}.store(in: &cancellables)
|
||||
|
||||
tutorialContext.isCallRunning = false
|
||||
tutorialContext.isCallIncoming = false
|
||||
action.fulfill()
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
|
||||
NSLog("Callkit CXAnswerCallAction -- Is main thread ? \(Thread.isMainThread ? "yes" : "no") ")
|
||||
do {
|
||||
// The audio stream is going to start shortly: the AVAudioSession must be configured now.
|
||||
// It is worth to note that an application does not have permission to configure the
|
||||
// AVAudioSession outside of this delegate action while it is running in background,
|
||||
// which is usually the case in an incoming call scenario.
|
||||
tutorialContext.mCore.configureAudioSession();
|
||||
try tutorialContext.mCall?.accept()
|
||||
tutorialContext.isCallRunning = true
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
tutorialContext.$mCore.receive(on: coreQueue).sink { core in
|
||||
do {
|
||||
// The audio stream is going to start shortly: the AVAudioSession must be configured now.
|
||||
// It is worth to note that an application does not have permission to configure the
|
||||
// AVAudioSession outside of this delegate action while it is running in background,
|
||||
// which is usually the case in an incoming call scenario.
|
||||
core?.configureAudioSession();
|
||||
try self.tutorialContext.mCall?.accept()
|
||||
LinphoneAsyncHelper.postOnMainQueue {
|
||||
self.tutorialContext.isCallRunning = true
|
||||
}
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
}.store(in: &cancellables)
|
||||
action.fulfill()
|
||||
}
|
||||
|
||||
@ -109,15 +117,15 @@ extension CallKitProviderDelegate: CXProviderDelegate {
|
||||
NSLog("Callkit didActivateaudiosession -- Is main thread ? \(Thread.isMainThread ? "yes" : "no") ")
|
||||
// The linphone Core must be notified that CallKit has activated the AVAudioSession
|
||||
// in order to start streaming audio.
|
||||
tutorialContext.linphoneAsyncHelper.postOnCoreQueue {
|
||||
self.tutorialContext.mCore.activateAudioSession(actived: true)
|
||||
}
|
||||
tutorialContext.$mCore.receive(on: coreQueue).sink { core in
|
||||
core?.activateAudioSession(actived: true)
|
||||
}.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
|
||||
// The linphone Core must be notified that CallKit has deactivated the AVAudioSession.
|
||||
tutorialContext.linphoneAsyncHelper.postOnCoreQueue {
|
||||
self.tutorialContext.mCore.activateAudioSession(actived: false)
|
||||
}
|
||||
tutorialContext.$mCore.receive(on: coreQueue).sink { core in
|
||||
core?.activateAudioSession(actived: false)
|
||||
}.store(in: &cancellables)
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,8 @@ import Combine
|
||||
|
||||
class CallKitExampleContext : ObservableObject
|
||||
{
|
||||
var mCore: Core!
|
||||
var mAccount: Account?
|
||||
var mIterateTimer : Timer!
|
||||
@Published var mCore: Core? = nil
|
||||
@Published var factory = Factory.Instance
|
||||
|
||||
@Published var coreVersion: String = Core.getVersion
|
||||
@Published var username : String = "quentindev"
|
||||
@ -30,6 +29,10 @@ class CallKitExampleContext : ObservableObject
|
||||
@Published var isSpeakerEnabled : Bool = false
|
||||
@Published var isMicrophoneEnabled : Bool = false
|
||||
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
var icancellables = Set<AnyCancellable>()
|
||||
|
||||
|
||||
/* Async */
|
||||
let linphoneAsyncHelper = LinphoneAsyncHelper()
|
||||
|
||||
@ -101,51 +104,41 @@ class CallKitExampleContext : ObservableObject
|
||||
init()
|
||||
{
|
||||
LoggingService.Instance.logLevel = LogLevel.Debug
|
||||
|
||||
// IMPORTANT : In this tutorial, we require the use of a core configuration file.
|
||||
// This way, once the registration is done, and until it is cleared, it will return to the LoggedIn state on launch.
|
||||
// This allows us to have a functional call when the app was closed and is started by a VOIP push notification (incoming call
|
||||
// We also need to enable "Push Notitifications" and "Background Mode - Voice Over IP"
|
||||
|
||||
linphoneAsyncHelper.postOnCoreQueue {
|
||||
let factory = Factory.Instance
|
||||
factory = Factory.Instance
|
||||
$factory.receive(on: coreQueue).sink { factory in
|
||||
let configDir = factory.getConfigDir(context: nil)
|
||||
let corePublisher = self.linphoneAsyncHelper.createLinphoneObjectWithPublisher(createAction: {
|
||||
try factory.createCore(configPath: "\(configDir)/MyConfig", factoryConfigPath: "", systemContext: nil)
|
||||
})
|
||||
corePublisher
|
||||
.postOnCoreQueue (
|
||||
onError: { error in
|
||||
NSLog("failed creating core \(error)")
|
||||
},
|
||||
receiveValue: { core in
|
||||
self.mCore = core
|
||||
// enabling push notifications management in the core
|
||||
self.mCore.callkitEnabled = true
|
||||
self.mCore.pushNotificationEnabled = true
|
||||
self.mCore.autoIterateEnabled = false
|
||||
self.addRegistrationStateCallBack(core: core)
|
||||
self.addCallStateChangedCallBack(core: core)
|
||||
try? core.start()
|
||||
})
|
||||
.postOnMainQueue { core in
|
||||
self.mIterateTimer = Timer.scheduledTimer(withTimeInterval: 0.02, repeats: true) { [weak self] timer in
|
||||
self?.linphoneAsyncHelper.postOnCoreQueue {
|
||||
core.iterate()
|
||||
}
|
||||
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() {
|
||||
|
||||
linphoneAsyncHelper.postOnCoreQueue {
|
||||
$factory.receive(on: coreQueue).sink { factory in
|
||||
do {
|
||||
var transport : TransportType
|
||||
if (self.transportType == "TLS") { transport = TransportType.Tls }
|
||||
@ -153,44 +146,42 @@ class CallKitExampleContext : ObservableObject
|
||||
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 accountParams = try self.mCore?.createAccountParams()
|
||||
let identity = try Factory.Instance.createAddress(addr: String("sip:" + self.username + "@" + self.domain))
|
||||
try! accountParams.setIdentityaddress(newValue: identity)
|
||||
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
|
||||
try accountParams?.setServeraddress(newValue: address)
|
||||
accountParams?.registerEnabled = true
|
||||
// Enable push notifications on this account
|
||||
accountParams.pushNotificationAllowed = true
|
||||
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"
|
||||
self.mAccount = try self.mCore.createAccount(params: accountParams)
|
||||
self.mCore.addAuthInfo(info: authInfo)
|
||||
try self.mCore.addAccount(account: self.mAccount!)
|
||||
self.mCore.defaultAccount = self.mAccount
|
||||
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()
|
||||
{
|
||||
linphoneAsyncHelper.postOnCoreQueue {
|
||||
if let account = self.mCore.defaultAccount {
|
||||
let params = account.params
|
||||
let clonedParams = params?.clone()
|
||||
clonedParams?.registerEnabled = false
|
||||
account.params = clonedParams
|
||||
}
|
||||
}
|
||||
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() {
|
||||
linphoneAsyncHelper.postOnCoreQueue {
|
||||
if let account = self.mCore.defaultAccount {
|
||||
self.mCore.removeAccount(account: account)
|
||||
self.mCore.clearAccounts()
|
||||
self.mCore.clearAllAuthInfo()
|
||||
}
|
||||
}
|
||||
$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)
|
||||
}
|
||||
}
|
||||
|
@ -58,18 +58,20 @@ public class LinphoneObjectsPublisher <T> : Publisher {
|
||||
|
||||
public class LinphoneAsyncHelper {
|
||||
|
||||
func postOnCoreQueue(lambda : @escaping ()->()) {
|
||||
|
||||
static func postOnCoreQueue(lambda : @escaping ()->()) {
|
||||
coreQueue.async {
|
||||
lambda()
|
||||
}
|
||||
}
|
||||
|
||||
func postOnMainQueue(lambda : @escaping()->()) {
|
||||
static func postOnMainQueue(lambda : @escaping()->()) {
|
||||
DispatchQueue.main.async {
|
||||
lambda()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Creates a publisher from the object created by the action passed as parameter
|
||||
// For example if passed a create core call this function will create the LinphoneObject Core on core queue, and created object will be published through the built publisher
|
||||
func createLinphoneObjectWithPublisher<LinphoneObject>(createAction:@escaping()throws -> LinphoneObject ) -> LinphoneObjectsPublisher<LinphoneObject> {
|
||||
@ -83,6 +85,7 @@ public class LinphoneAsyncHelper {
|
||||
}
|
||||
return publisher
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
@ -117,5 +120,83 @@ extension Core {
|
||||
// ...
|
||||
}
|
||||
|
||||
/*
|
||||
extension Factory {
|
||||
static var linphoneObjectsPublishers = [String:LinphoneObjectsPublisher<Factory>]()
|
||||
var publisher: LinphoneObjectsPublisher<Factory> {
|
||||
get {
|
||||
let ref = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
||||
if (Factory.linphoneObjectsPublishers[ref] == nil) {
|
||||
let publisher = LinphoneObjectsPublisher<Factory>()
|
||||
Factory.linphoneObjectsPublishers[ref] = publisher
|
||||
}
|
||||
coreQueue.async {
|
||||
Factory.linphoneObjectsPublishers[ref]!.passThroughSubject.send(self)
|
||||
}
|
||||
return Factory.linphoneObjectsPublishers[ref]!
|
||||
}
|
||||
set(newValue) {
|
||||
let ref = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
||||
Factory.linphoneObjectsPublishers[ref] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
extension LinphoneObject {
|
||||
static var linphoneObjectsPublishers = [String:LinphoneObjectsPublisher<LinphoneObject>]()
|
||||
private func refFromSelf() -> String {
|
||||
return String(format: "%p", unsafeBitCast(self, to: Int.self))
|
||||
}
|
||||
var publisher: LinphoneObjectsPublisher<LinphoneObject> {
|
||||
get {
|
||||
let ref = refFromSelf()
|
||||
if (LinphoneObject.linphoneObjectsPublishers[ref] == nil) {
|
||||
let publisher = LinphoneObjectsPublisher<LinphoneObject>()
|
||||
LinphoneObject.linphoneObjectsPublishers[ref] = publisher
|
||||
}
|
||||
coreQueue.async {
|
||||
LinphoneObject.linphoneObjectsPublishers[ref]!.passThroughSubject.send(self)
|
||||
}
|
||||
return LinphoneObject.linphoneObjectsPublishers[ref]!
|
||||
}
|
||||
set(newValue) {
|
||||
let ref = refFromSelf()
|
||||
LinphoneObject.linphoneObjectsPublishers[ref] = newValue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Could not find a way to put the below inside the generic LinphoneObject extension
|
||||
|
||||
extension Factory {
|
||||
func postOnCoreQueue(lambda:@escaping ((Factory) -> Void)) {
|
||||
publisher.postOnCoreQueue { result in
|
||||
lambda(result as! Factory)
|
||||
}
|
||||
}
|
||||
func postOnMainQueue(lambda:@escaping ((Factory) -> Void)) {
|
||||
publisher.postOnMainQueue { result in
|
||||
lambda(result as! Factory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Core {
|
||||
func postOnCoreQueue(lambda:@escaping ((Core) -> Void)) {
|
||||
publisher.postOnCoreQueue { result in
|
||||
lambda(result as! Core)
|
||||
}
|
||||
}
|
||||
func postOnMainQueue(lambda:@escaping ((Core) -> Void)) {
|
||||
publisher.postOnMainQueue { result in
|
||||
lambda(result as! Core)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// .. add extensions to other needed objects, or have this generated in the wrapper
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user