// // 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() // A publisher object that can old one or many LinphoneObject objects. public class LinphoneObjectsPublisher : Publisher { public typealias Output = T public typealias Failure = Error let passThroughSubject = PassthroughSubject() public func receive(subscriber: S) where S : Subscriber, S.Failure == Failure, S.Input == Output { passThroughSubject.receive(subscriber: subscriber) } public func send(completion: Subscribers.Completion) { passThroughSubject.send(completion: completion) } @discardableResult public func postOnMainQueue(onError :@escaping ((Error) -> Void) = {_ in }, receiveValue:@escaping ((Output) -> Void)) -> LinphoneObjectsPublisher { doOnQueue(onError,receiveValue,queue: DispatchQueue.main) return self } @discardableResult public func postOnCoreQueue(onError :@escaping ((Error) -> Void) = {_ in }, receiveValue:@escaping ((Output) -> Void)) -> LinphoneObjectsPublisher { 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 { func postOnCoreQueue(lambda : @escaping ()->()) { coreQueue.async { lambda() } } 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(createAction:@escaping()throws -> LinphoneObject ) -> LinphoneObjectsPublisher { let publisher = LinphoneObjectsPublisher() 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 } // ... }