Suggestions from CD3 revised call back publishers
This commit is contained in:
@@ -69,7 +69,7 @@ extension CallKitProviderDelegate: CXProviderDelegate {
|
||||
if (self.tutorialContext.mCall?.state != .End && self.tutorialContext.mCall?.state != .Released) {
|
||||
try self.tutorialContext.mCall?.terminate()
|
||||
}
|
||||
LinphoneAsyncHelper.postOnMainQueue {
|
||||
DispatchQueue.main.async {
|
||||
self.tutorialContext.isCallRunning = false
|
||||
self.tutorialContext.isCallIncoming = false
|
||||
}
|
||||
@@ -89,7 +89,7 @@ extension CallKitProviderDelegate: CXProviderDelegate {
|
||||
// which is usually the case in an incoming call scenario.
|
||||
core?.configureAudioSession();
|
||||
try self.tutorialContext.mCall?.accept()
|
||||
LinphoneAsyncHelper.postOnMainQueue {
|
||||
DispatchQueue.main.async {
|
||||
self.tutorialContext.isCallRunning = true
|
||||
}
|
||||
} catch {
|
||||
|
||||
@@ -17,8 +17,8 @@ class CallKitExampleContext : ObservableObject
|
||||
@Published var factory = Factory.Instance
|
||||
|
||||
@Published var coreVersion: String = Core.getVersion
|
||||
@Published var username : String = "quentindev"
|
||||
@Published var passwd : String = "dev"
|
||||
@Published var username : String = "cd1"
|
||||
@Published var passwd : String = "cd1"
|
||||
@Published var domain : String = "sip.linphone.org"
|
||||
@Published var loggedIn: Bool = false
|
||||
@Published var transportType : String = "TLS"
|
||||
@@ -33,10 +33,6 @@ class CallKitExampleContext : ObservableObject
|
||||
var icancellables = Set<AnyCancellable>()
|
||||
|
||||
|
||||
/* Async */
|
||||
let linphoneAsyncHelper = LinphoneAsyncHelper()
|
||||
|
||||
|
||||
/*------------ Callkit tutorial related variables ---------------*/
|
||||
let incomingCallName = "Incoming call example"
|
||||
var mCall : Call?
|
||||
@@ -45,48 +41,49 @@ class CallKitExampleContext : ObservableObject
|
||||
|
||||
|
||||
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) {
|
||||
|
||||
core.accountStatePublisher
|
||||
.postOnMainQueue { (publishedValue:(core: Core, account: Account, state: RegistrationState, message: String)) in
|
||||
NSLog("New registration state is \(publishedValue.state) for user id \( String(describing: publishedValue.account.params?.identityAddress?.asString()))\n")
|
||||
if (publishedValue.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) {
|
||||
NSLog("Account registered Push voip token: \(String(describing: publishedValue.account.params?.pushNotificationConfig?.voipToken))")
|
||||
} else if (publishedValue.state == .Cleared) {
|
||||
self.loggedIn = false
|
||||
}
|
||||
}
|
||||
.postOnCoreQueue{ result in
|
||||
.postOnCoreQueue{ (accountStatePublisher:(core: Core, account: Account, state: RegistrationState, message: String)) 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){
|
||||
core.callStatePublisher
|
||||
.postOnMainQueue { (publishedValue:(core: Core, call: Call, state: Call.State, message: String)) in
|
||||
self.callMsg = publishedValue.message
|
||||
if (publishedValue.state == .PushIncomingReceived){
|
||||
// We're being called by someone (and app is in background)
|
||||
self.mCall = result.call
|
||||
self.mCall = publishedValue.call
|
||||
self.mProviderDelegate.incomingCall()
|
||||
self.isCallIncoming = true
|
||||
self.callMsg = result.message
|
||||
} else if (result.state == .IncomingReceived) {
|
||||
self.callMsg = publishedValue.message
|
||||
} else if (publishedValue.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.mCall = publishedValue.call
|
||||
self.mProviderDelegate.incomingCall()
|
||||
|
||||
self.isCallIncoming = true
|
||||
self.callMsg = result.message
|
||||
self.callMsg = publishedValue.message
|
||||
}
|
||||
self.remoteAddress = result.call.remoteAddress!.asStringUriOnly()
|
||||
} else if (result.state == .Connected) {
|
||||
self.remoteAddress = publishedValue.call.remoteAddress!.asStringUriOnly()
|
||||
} else if (publishedValue.state == .Connected) {
|
||||
self.isCallIncoming = false
|
||||
self.isCallRunning = true
|
||||
} else if (result.state == .Released || result.state == .End || result.state == .Error) {
|
||||
} else if (publishedValue.state == .Released || publishedValue.state == .End || publishedValue.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
|
||||
@@ -96,7 +93,7 @@ class CallKitExampleContext : ObservableObject
|
||||
self.remoteAddress = "Nobody yet"
|
||||
}
|
||||
}
|
||||
.postOnCoreQueue{ result in
|
||||
.postOnCoreQueue{ (callStatePublisher:(core: Core, call: Call, state: Call.State, message: String)) in
|
||||
// optional something on core queue if needed
|
||||
}
|
||||
}
|
||||
@@ -115,7 +112,7 @@ class CallKitExampleContext : ObservableObject
|
||||
self.addRegistrationStateCallBack(core: core)
|
||||
self.addCallStateChangedCallBack(core: core)
|
||||
try? core.start()
|
||||
LinphoneAsyncHelper.postOnMainQueue {
|
||||
DispatchQueue.main.async {
|
||||
self.mCore = core // @Publisher assignment can be done on main queue only
|
||||
}
|
||||
} catch {
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
//
|
||||
// LinphoneAsyncWrapper.swift
|
||||
// CallKitTutorial
|
||||
//
|
||||
// Created by CD on 13/09/2023.
|
||||
// Copyright © 2023 BelledonneCommunications. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import linphonesw
|
||||
|
||||
var coreQueue : DispatchQueue = DispatchQueue(label:"core.queue")
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
|
||||
// A publisher object that can old one or many LinphoneObject objects.
|
||||
|
||||
public class LinphoneObjectsPublisher <T> : Publisher {
|
||||
public typealias Output = T
|
||||
public typealias Failure = Error
|
||||
let passThroughSubject = PassthroughSubject<Output, Failure>()
|
||||
public func receive<S>(subscriber: S) where S : Subscriber, S.Failure == Failure, S.Input == Output {
|
||||
passThroughSubject.receive(subscriber: subscriber)
|
||||
}
|
||||
|
||||
public func send(completion: Subscribers.Completion<Failure>) {
|
||||
passThroughSubject.send(completion: completion)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func postOnMainQueue(onError :@escaping ((Error) -> Void) = {_ in }, receiveValue:@escaping ((Output) -> Void)) -> LinphoneObjectsPublisher <T>{
|
||||
doOnQueue(onError,receiveValue,queue: DispatchQueue.main)
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func postOnCoreQueue(onError :@escaping ((Error) -> Void) = {_ in }, receiveValue:@escaping ((Output) -> Void)) -> LinphoneObjectsPublisher <T>{
|
||||
doOnQueue(onError,receiveValue,queue: coreQueue)
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
private func doOnQueue(_ onError :@escaping ((Error) -> Void) = {_ in }, _ receiveValue:@escaping ((Output) -> Void), queue:DispatchQueue) {
|
||||
passThroughSubject.receive(on:queue)
|
||||
.sink { error in
|
||||
onError(error as! Error)
|
||||
} receiveValue: { result in
|
||||
receiveValue(result)
|
||||
}.store(in: &cancellables)
|
||||
}
|
||||
|
||||
var savedReference : Any? = nil // Used when a reference is needed to avoid object from beeing GCd (example delegate stubs)
|
||||
convenience init (reference: Any) {
|
||||
self.init()
|
||||
savedReference = reference
|
||||
}
|
||||
}
|
||||
|
||||
public class LinphoneAsyncHelper {
|
||||
|
||||
|
||||
static func postOnCoreQueue(lambda : @escaping ()->()) {
|
||||
coreQueue.async {
|
||||
lambda()
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
let publisher = LinphoneObjectsPublisher<LinphoneObject>()
|
||||
coreQueue.async {
|
||||
do {
|
||||
publisher.passThroughSubject.send(try createAction())
|
||||
} catch {
|
||||
publisher.send(completion: .failure(error))
|
||||
}
|
||||
}
|
||||
return publisher
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
extension Core {
|
||||
|
||||
// Methods below would generated by a script similar to the one that creates LinphoneWrapper
|
||||
|
||||
public func createAccountRegistrationStateChangedPublisher() -> LinphoneObjectsPublisher<(core:Core, account:Account, state:RegistrationState, message:String)> {
|
||||
let publisher = LinphoneObjectsPublisher<(core:Core, account:Account, state:RegistrationState, message:String)>()
|
||||
let coreDelegate = CoreDelegateStub (
|
||||
onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in
|
||||
publisher.passThroughSubject.send((core,account,state,message))
|
||||
})
|
||||
publisher.savedReference = coreDelegate
|
||||
addDelegate(delegate: coreDelegate)
|
||||
return publisher
|
||||
}
|
||||
|
||||
public func createOnCallStateChangedPublisher() -> LinphoneObjectsPublisher<(core: Core, call: Call, state: Call.State, message: String)> {
|
||||
let publisher = LinphoneObjectsPublisher<(core: Core, call: Call, state: Call.State, message: String)>()
|
||||
let coreDelegate = CoreDelegateStub (
|
||||
onCallStateChanged: { (core: Core, call: Call, state: Call.State, message: String) in
|
||||
publisher.passThroughSubject.send((core,call,state,message))
|
||||
})
|
||||
publisher.savedReference = coreDelegate
|
||||
addDelegate(delegate: coreDelegate)
|
||||
return publisher
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user