diff --git a/Linphone.xcodeproj/project.pbxproj b/Linphone.xcodeproj/project.pbxproj index 565fc4601..14382a983 100644 --- a/Linphone.xcodeproj/project.pbxproj +++ b/Linphone.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 886500223A8E518D3EE5FCB7 /* Pods_Linphone.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A334B8FDAD2893691A734BE /* Pods_Linphone.framework */; }; D706BA822ADD72D100278F45 /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D706BA812ADD72D100278F45 /* DeviceRotationViewModifier.swift */; }; D70C93DE2AC2D0F60063CA3B /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = D70C93DD2AC2D0F60063CA3B /* Localizable.xcstrings */; }; D717071E2AC5922E0037746F /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D717071D2AC5922E0037746F /* ColorExtension.swift */; }; @@ -71,8 +70,6 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 1A334B8FDAD2893691A734BE /* Pods_Linphone.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Linphone.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 1DE4CD5FD6E1F01639F27E3B /* Pods-Linphone.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Linphone.release.xcconfig"; path = "Target Support Files/Pods-Linphone/Pods-Linphone.release.xcconfig"; sourceTree = ""; }; D706BA812ADD72D100278F45 /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = ""; }; D70C93DD2AC2D0F60063CA3B /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; D717071D2AC5922E0037746F /* ColorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorExtension.swift; sourceTree = ""; }; @@ -136,7 +133,6 @@ D7E6D0542AEBFCCE00A57AAF /* ContactsInnerFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsInnerFragment.swift; sourceTree = ""; }; D7EAACCE2AD6ED8000AA6A8A /* PermissionsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionsFragment.swift; sourceTree = ""; }; D7FB55102AD447FD00A5AB15 /* RegisterFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegisterFragment.swift; sourceTree = ""; }; - FB718F405DAF7B9993AEB878 /* Pods-Linphone.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Linphone.debug.xcconfig"; path = "Target Support Files/Pods-Linphone/Pods-Linphone.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -144,26 +140,15 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 886500223A8E518D3EE5FCB7 /* Pods_Linphone.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 1CD95087B17CAD149119B7C2 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 1A334B8FDAD2893691A734BE /* Pods_Linphone.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; A31AF2AB8C6A3D7B7EA3B424 /* Pods */ = { isa = PBXGroup; children = ( - FB718F405DAF7B9993AEB878 /* Pods-Linphone.debug.xcconfig */, - 1DE4CD5FD6E1F01639F27E3B /* Pods-Linphone.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -187,7 +172,6 @@ D719ABB52ABC67BF00B41C10 /* Linphone */, D719ABB42ABC67BF00B41C10 /* Products */, A31AF2AB8C6A3D7B7EA3B424 /* Pods */, - 1CD95087B17CAD149119B7C2 /* Frameworks */, ); sourceTree = ""; }; @@ -422,12 +406,10 @@ isa = PBXNativeTarget; buildConfigurationList = D719ABC22ABC67BF00B41C10 /* Build configuration list for PBXNativeTarget "Linphone" */; buildPhases = ( - BE9432280D0A11AA770A50FD /* [CP] Check Pods Manifest.lock */, D719ABAF2ABC67BF00B41C10 /* Sources */, D719ABB02ABC67BF00B41C10 /* Frameworks */, D719ABB12ABC67BF00B41C10 /* Resources */, D7FB55122AD53FE200A5AB15 /* Run Script */, - D5CA1ECD620857DB91E334A5 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -491,45 +473,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - BE9432280D0A11AA770A50FD /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Linphone-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - D5CA1ECD620857DB91E334A5 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Linphone/Pods-Linphone-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Linphone/Pods-Linphone-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Linphone/Pods-Linphone-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; D7FB55122AD53FE200A5AB15 /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -617,6 +560,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOLS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -677,6 +621,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOLS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -728,7 +673,6 @@ }; D719ABC32ABC67BF00B41C10 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FB718F405DAF7B9993AEB878 /* Pods-Linphone.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -773,7 +717,6 @@ }; D719ABC42ABC67BF00B41C10 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 1DE4CD5FD6E1F01639F27E3B /* Pods-Linphone.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; diff --git a/Linphone/Contacts/ContactsManager.swift b/Linphone/Contacts/ContactsManager.swift index 29506bbd5..5ff02becb 100644 --- a/Linphone/Contacts/ContactsManager.swift +++ b/Linphone/Contacts/ContactsManager.swift @@ -40,16 +40,16 @@ final class ContactsManager: ObservableObject { } func fetchContacts() { - DispatchQueue.global().async { - if self.coreContext.mCore.globalState == GlobalState.Shutdown || self.coreContext.mCore.globalState == GlobalState.Off { + coreContext.doOnCoreQueue { core in + if core.globalState == GlobalState.Shutdown || core.globalState == GlobalState.Off { print("$TAG Core is being stopped or already destroyed, abort") } else { print("$TAG ${friends.size} friends created") - self.friendList = self.coreContext.mCore.getFriendListByName(name: self.nativeAddressBookFriendList) + self.friendList = core.getFriendListByName(name: self.nativeAddressBookFriendList) if self.friendList == nil { do { - self.friendList = try self.coreContext.mCore.createFriendList() + self.friendList = try core.createFriendList() } catch let error { print("Failed to enumerate contact", error) } @@ -63,7 +63,7 @@ final class ContactsManager: ObservableObject { self.friendList!.databaseStorageEnabled = false // We don't want to store local address-book in DB self.friendList!.displayName = self.nativeAddressBookFriendList - self.coreContext.mCore.addFriendList(list: self.friendList!) + core.addFriendList(list: self.friendList!) } else { print( "$TAG Friend list [$LINPHONE_ADDRESS_BOOK_FRIEND_LIST] found, removing existing friends if any" @@ -73,10 +73,10 @@ final class ContactsManager: ObservableObject { } } - self.linphoneFriendList = self.coreContext.mCore.getFriendListByName(name: self.linphoneAddressBookFirendList) + self.linphoneFriendList = core.getFriendListByName(name: self.linphoneAddressBookFirendList) if self.linphoneFriendList == nil { do { - self.linphoneFriendList = try self.coreContext.mCore.createFriendList() + self.linphoneFriendList = try core.createFriendList() } catch let error { print("Failed to enumerate contact", error) } @@ -90,7 +90,7 @@ final class ContactsManager: ObservableObject { self.linphoneFriendList!.databaseStorageEnabled = true self.linphoneFriendList!.displayName = self.linphoneAddressBookFirendList - self.coreContext.mCore.addFriendList(list: self.linphoneFriendList!) + core.addFriendList(list: self.linphoneFriendList!) } } @@ -200,66 +200,69 @@ final class ContactsManager: ObservableObject { } func saveFriend(result: String, contact: Contact, existingFriend: Friend?) -> Friend? { - do { - let friend = (existingFriend != nil) ? existingFriend : try self.coreContext.mCore.createFriend() - - if friend != nil { - friend!.edit() + + self.coreContext.doOnCoreQueue() { core in + do { + let friend = (existingFriend != nil) ? existingFriend : try core.createFriend() - friend!.nativeUri = contact.identifier - - try friend!.setName(newValue: contact.firstName + " " + contact.lastName) - - let friendvCard = friend!.vcard - - if friendvCard != nil { - friendvCard!.givenName = contact.firstName - friendvCard!.familyName = contact.lastName - } - - friend!.organization = contact.organizationName - - var friendAddresses: [Address] = [] - friend?.addresses.forEach({ address in - friend?.removeAddress(address: address) - }) - contact.sipAddresses.forEach { sipAddress in - let address = self.coreContext.mCore.interpretUrl(url: sipAddress, applyInternationalPrefix: true) + if friend != nil { + friend!.edit() - if address != nil && ((friendAddresses.firstIndex(where: {$0.asString() == address?.asString()})) == nil) { - friend!.addAddress(address: address!) - friendAddresses.append(address!) + friend!.nativeUri = contact.identifier + + try friend!.setName(newValue: contact.firstName + " " + contact.lastName) + + let friendvCard = friend!.vcard + + if friendvCard != nil { + friendvCard!.givenName = contact.firstName + friendvCard!.familyName = contact.lastName } - } - - var friendPhoneNumbers: [PhoneNumber] = [] - friend?.phoneNumbersWithLabel.forEach({ phoneNumber in - friend?.removePhoneNumberWithLabel(phoneNumber: phoneNumber) - }) - contact.phoneNumbers.forEach { phone in - do { - if (friendPhoneNumbers.firstIndex(where: {$0.num == phone.num})) == nil { - let labelDrop = String(phone.numLabel.dropFirst(4).dropLast(4)) - let phoneNumber = try Factory.Instance.createFriendPhoneNumber(phoneNumber: phone.num, label: labelDrop) - friend!.addPhoneNumberWithLabel(phoneNumber: phoneNumber) - friendPhoneNumbers.append(phone) + + friend!.organization = contact.organizationName + + var friendAddresses: [Address] = [] + friend?.addresses.forEach({ address in + friend?.removeAddress(address: address) + }) + contact.sipAddresses.forEach { sipAddress in + let address = core.interpretUrl(url: sipAddress, applyInternationalPrefix: true) + + if address != nil && ((friendAddresses.firstIndex(where: {$0.asString() == address?.asString()})) == nil) { + friend!.addAddress(address: address!) + friendAddresses.append(address!) } - } catch let error { - print("Failed to enumerate contact", error) } + + var friendPhoneNumbers: [PhoneNumber] = [] + friend?.phoneNumbersWithLabel.forEach({ phoneNumber in + friend?.removePhoneNumberWithLabel(phoneNumber: phoneNumber) + }) + contact.phoneNumbers.forEach { phone in + do { + if (friendPhoneNumbers.firstIndex(where: {$0.num == phone.num})) == nil { + let labelDrop = String(phone.numLabel.dropFirst(4).dropLast(4)) + let phoneNumber = try Factory.Instance.createFriendPhoneNumber(phoneNumber: phone.num, label: labelDrop) + friend!.addPhoneNumberWithLabel(phoneNumber: phoneNumber) + friendPhoneNumbers.append(phone) + } + } catch let error { + print("Failed to enumerate contact", error) + } + } + + friend!.photo = "file:/" + result + + friend!.organization = contact.organizationName + friend!.jobTitle = contact.jobTitle + + friend!.done() + return friend } - - friend!.photo = "file:/" + result - - friend!.organization = contact.organizationName - friend!.jobTitle = contact.jobTitle - - friend!.done() - return friend + } catch let error { + print("Failed to enumerate contact", error) + return nil } - } catch let error { - print("Failed to enumerate contact", error) - return nil } return nil } diff --git a/Linphone/Core/CoreContext.swift b/Linphone/Core/CoreContext.swift index efa6b73af..a68b25127 100644 --- a/Linphone/Core/CoreContext.swift +++ b/Linphone/Core/CoreContext.swift @@ -17,74 +17,104 @@ * along with this program. If not, see . */ +// swiftlint:disable large_tuple import linphonesw +import Combine final class CoreContext: ObservableObject { static let shared = CoreContext() - var mCore: Core! - var mRegistrationDelegate: CoreDelegate! - var mConfigurationDelegate: CoreDelegate! - var coreVersion: String = Core.getVersion @Published var loggedIn: Bool = false @Published var loggingInProgress: Bool = false @Published var toastMessage: String = "" + @Published var defaultAccount: Account? + + private var mCore: Core! + private var mIteratePublisher: AnyCancellable? private init() {} - func initialiseCore() async throws { + func doOnCoreQueue(synchronous : Bool = false, lambda: @escaping (Core) -> Void) { + if synchronous { + coreQueue.sync { + lambda(self.mCore) + } + } else { + coreQueue.async { + lambda(self.mCore) + } + } + } + + func initialiseCore() throws { LoggingService.Instance.logLevel = LogLevel.Debug - let factory = Factory.Instance - let configDir = factory.getConfigDir(context: nil) - try? mCore = Factory.Instance.createCore(configPath: "\(configDir)/MyConfig", factoryConfigPath: "", systemContext: nil) - - mCore.friendsDatabasePath = "\(configDir)/friends.db" - - try? mCore.start() - - // Create a Core listener to listen for the callback we need - // In this case, we want to know about the account registration status - - mRegistrationDelegate = - CoreDelegateStub( - onConfiguringStatus: {(_: Core, state: Config.ConfiguringState, message: String) in - NSLog("New configuration state is \(state) = \(message)\n") - if state == .Successful { + coreQueue.async { + let configDir = Factory.Instance.getConfigDir(context: nil) + try? self.mCore = Factory.Instance.createCore(configPath: "\(configDir)/MyConfig", factoryConfigPath: "", systemContext: nil) + self.mCore.autoIterateEnabled = false + self.mCore.friendsDatabasePath = "\(configDir)/friends.db" + + self.mCore.publisher?.onGlobalStateChanged?.postOnMainQueue { (cbVal: (core: Core, state: GlobalState, message: String)) in + if cbVal.state == GlobalState.On { + self.defaultAccount = self.mCore.defaultAccount + } else if cbVal.state == GlobalState.Off { + self.defaultAccount = nil + } + } + try? self.mCore.start() + + // Create a Core listener to listen for the callback we need + // In this case, we want to know about the account registration status + self.mCore.publisher?.onConfiguringStatus?.postOnMainQueue { (cbVal: (core: Core, status: Config.ConfiguringState, message: String)) in + NSLog("New configuration state is \(cbVal.status) = \(cbVal.message)\n") + if cbVal.status == Config.ConfiguringState.Successful { self.toastMessage = "Successful" } else { self.toastMessage = "Failed" } - }, + } - onAccountRegistrationStateChanged: {(_: Core, account: Account, state: RegistrationState, message: String) in + self.mCore.publisher?.onAccountRegistrationStateChanged?.postOnMainQueue { (cbVal: (core: Core, account: Account, state: RegistrationState, message: String)) in // If account has been configured correctly, we will go through Progress and Ok states // Otherwise, we will be Failed. - NSLog("New registration state is \(state) for user id \( String(describing: account.params?.identityAddress?.asString())) = \(message)\n") - if state == .Ok { + NSLog("New registration state is \(cbVal.state) for user id " + + "\( String(describing: cbVal.account.params?.identityAddress?.asString())) = \(cbVal.message)\n") + if cbVal.state == .Ok { self.loggingInProgress = false self.loggedIn = true - } else if state == .Progress { + } else if cbVal.state == .Progress { self.loggingInProgress = true } else { self.toastMessage = "Registration failed" self.loggingInProgress = false self.loggedIn = false - - let params = account.params + } + }.postOnCoreQueue { (cbVal: (core: Core, account: Account, state: RegistrationState, message: String)) in + // If registration failed, remove account from core + if cbVal.state != .Ok && cbVal.state != .Progress { + let params = cbVal.account.params let clonedParams = params?.clone() clonedParams?.registerEnabled = false - account.params = clonedParams + cbVal.account.params = clonedParams - self.mCore!.removeAccount(account: account) - self.mCore!.clearAccounts() - self.mCore!.clearAllAuthInfo() + cbVal.core.removeAccount(account: cbVal.account) + cbVal.core.clearAccounts() + cbVal.core.clearAllAuthInfo() } } - ) - - mCore.addDelegate(delegate: mRegistrationDelegate) + + self.mIteratePublisher = Timer.publish(every: 0.02, on: .main, in: .common) + .autoconnect() + .receive(on: coreQueue) + .sink { _ in + self.mCore.iterate() + } + + } } } + +// swiftlint:enable large_tuple diff --git a/Linphone/LinphoneApp.swift b/Linphone/LinphoneApp.swift index 5aa85a39b..85f14b81c 100644 --- a/Linphone/LinphoneApp.swift +++ b/Linphone/LinphoneApp.swift @@ -32,10 +32,10 @@ struct LinphoneApp: App { if isActive { if !sharedMainViewModel.welcomeViewDisplayed { WelcomeView(sharedMainViewModel: sharedMainViewModel) - } else if coreContext.mCore.defaultAccount == nil || sharedMainViewModel.displayProfileMode { + } else if coreContext.defaultAccount == nil || sharedMainViewModel.displayProfileMode { AssistantView(sharedMainViewModel: sharedMainViewModel) .toast(isShowing: $coreContext.toastMessage) - } else if coreContext.mCore.defaultAccount != nil { + } else if coreContext.defaultAccount != nil { ContentView(contactViewModel: ContactViewModel(), editContactViewModel: EditContactViewModel(), historyViewModel: HistoryViewModel()) .toast(isShowing: $coreContext.toastMessage) } diff --git a/Linphone/SplashScreen.swift b/Linphone/SplashScreen.swift index 64d59e258..350fc93ad 100644 --- a/Linphone/SplashScreen.swift +++ b/Linphone/SplashScreen.swift @@ -40,7 +40,7 @@ struct SplashScreen: View { .ignoresSafeArea(.all) .onAppear { Task { - try await coreContext.initialiseCore() + try coreContext.initialiseCore() withAnimation { self.isActive = true } diff --git a/Linphone/UI/Assistant/Viewmodel/AccountLoginViewModel.swift b/Linphone/UI/Assistant/Viewmodel/AccountLoginViewModel.swift index 6f778145a..d85b4233e 100644 --- a/Linphone/UI/Assistant/Viewmodel/AccountLoginViewModel.swift +++ b/Linphone/UI/Assistant/Viewmodel/AccountLoginViewModel.swift @@ -33,83 +33,95 @@ class AccountLoginViewModel: ObservableObject { init() {} func login() { - do { - // Get the transport protocol to use. - // TLS is strongly recommended - // Only use UDP if you don't have the choice - var transport: TransportType - if transportType == "TLS" { - transport = TransportType.Tls - } else if transportType == "TCP" { - transport = TransportType.Tcp - } else { transport = TransportType.Udp } - - // To configure a SIP account, we need an Account object and an AuthInfo object - // The first one is how to connect to the proxy server, the second one stores the credentials - - // The auth info can be created from the Factory as it's only a data class - // userID is set to null as it's the same as the username in our case - // ha1 is set to null as we are using the clear text password. Upon first register, the hash will be computed automatically. - // The realm will be determined automatically from the first register, as well as the algorithm - let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain) - - // Account object replaces deprecated ProxyConfig object - // Account object is configured through an AccountParams object that we can obtain from the Core - - let accountParams = try coreContext.mCore!.createAccountParams() - - // A SIP account is identified by an identity address that we can construct from the username and domain - let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain)) - try accountParams.setIdentityaddress(newValue: identity) - - // We also need to configure where the proxy server is located - let address = try Factory.Instance.createAddress(addr: String("sip:" + domain)) - - // We use the Address object to easily set the transport protocol - try address.setTransport(newValue: transport) - try accountParams.setServeraddress(newValue: address) - // And we ensure the account will start the registration process - accountParams.registerEnabled = true - - // Now that our AccountParams is configured, we can create the Account object - let account = try coreContext.mCore!.createAccount(params: accountParams) - - // Now let's add our objects to the Core - coreContext.mCore!.addAuthInfo(info: authInfo) - try coreContext.mCore!.addAccount(account: account) - - // Also set the newly added account as default - coreContext.mCore!.defaultAccount = account - - } catch { NSLog(error.localizedDescription) } + coreContext.doOnCoreQueue { core in + do { + // Get the transport protocol to use. + // TLS is strongly recommended + // Only use UDP if you don't have the choice + var transport: TransportType + if self.transportType == "TLS" { + transport = TransportType.Tls + } else if self.transportType == "TCP" { + transport = TransportType.Tcp + } else { transport = TransportType.Udp } + + // To configure a SIP account, we need an Account object and an AuthInfo object + // The first one is how to connect to the proxy server, the second one stores the credentials + + // The auth info can be created from the Factory as it's only a data class + // userID is set to null as it's the same as the username in our case + // ha1 is set to null as we are using the clear text password. Upon first register, the hash will be computed automatically. + // The realm will be determined automatically from the first register, as well as the algorithm + let authInfo = try Factory.Instance.createAuthInfo(username: self.username, userid: "", passwd: self.passwd, ha1: "", realm: "", domain: self.domain) + + // Account object replaces deprecated ProxyConfig object + // Account object is configured through an AccountParams object that we can obtain from the Core + + let accountParams = try core.createAccountParams() + + // A SIP account is identified by an identity address that we can construct from the username and domain + let identity = try Factory.Instance.createAddress(addr: String("sip:" + self.username + "@" + self.domain)) + try accountParams.setIdentityaddress(newValue: identity) + + // We also need to configure where the proxy server is located + let address = try Factory.Instance.createAddress(addr: String("sip:" + self.domain)) + + // We use the Address object to easily set the transport protocol + try address.setTransport(newValue: transport) + try accountParams.setServeraddress(newValue: address) + // And we ensure the account will start the registration process + accountParams.registerEnabled = true + + // Now that our AccountParams is configured, we can create the Account object + let account = try core.createAccount(params: accountParams) + + // Now let's add our objects to the Core + core.addAuthInfo(info: authInfo) + try core.addAccount(account: account) + + // Also set the newly added account as default + core.defaultAccount = account + DispatchQueue.main.async { + self.coreContext.defaultAccount = account + } + + } catch { NSLog(error.localizedDescription) } + } } func unregister() { - // Here we will disable the registration of our Account - if let account = coreContext.mCore!.defaultAccount { - - let params = account.params - // Returned params object is const, so to make changes we first need to clone it - let clonedParams = params?.clone() - - // Now let's make our changes - clonedParams?.registerEnabled = false - - // And apply them - account.params = clonedParams + coreContext.doOnCoreQueue { core in + // Here we will disable the registration of our Account + if let account = core.defaultAccount { + + let params = account.params + // Returned params object is const, so to make changes we first need to clone it + let clonedParams = params?.clone() + + // Now let's make our changes + clonedParams?.registerEnabled = false + + // And apply them + account.params = clonedParams + } } } func delete() { - // To completely remove an Account - if let account = coreContext.mCore!.defaultAccount { - coreContext.mCore!.removeAccount(account: account) - - // To remove all accounts use - coreContext.mCore!.clearAccounts() - - // Same for auth info - coreContext.mCore!.clearAllAuthInfo() + coreContext.doOnCoreQueue { core in + // To completely remove an Account + if let account = core.defaultAccount { + core.removeAccount(account: account) + DispatchQueue.main.async { + self.coreContext.defaultAccount = nil + } + + // To remove all accounts use + core.clearAccounts() + + // Same for auth info + core.clearAllAuthInfo() + } } } } diff --git a/Linphone/UI/Assistant/Viewmodel/QRScanner.swift b/Linphone/UI/Assistant/Viewmodel/QRScanner.swift index d5dd78159..5d546ef96 100644 --- a/Linphone/UI/Assistant/Viewmodel/QRScanner.swift +++ b/Linphone/UI/Assistant/Viewmodel/QRScanner.swift @@ -70,14 +70,11 @@ class Coordinator: NSObject, AVCaptureMetadataOutputObjectsDelegate { if let url = NSURL(string: result) { if UIApplication.shared.canOpenURL(url as URL) { lastResult = result - do { - try coreContext.mCore.setProvisioninguri(newValue: result) - coreContext.mCore.stop() - try coreContext.mCore.start() - } catch { - + coreContext.doOnCoreQueue { core in + try? core.setProvisioninguri(newValue: result) + core.stop() + try? core.start() } - } else { coreContext.toastMessage = "Invalide URI" } diff --git a/Linphone/Utils/MagicSearchSingleton.swift b/Linphone/Utils/MagicSearchSingleton.swift index 2ee38c8cc..50c9f02e9 100644 --- a/Linphone/Utils/MagicSearchSingleton.swift +++ b/Linphone/Utils/MagicSearchSingleton.swift @@ -25,9 +25,8 @@ final class MagicSearchSingleton: ObservableObject { private var coreContext = CoreContext.shared private var magicSearch: MagicSearch! - var magicSearchDelegate: MagicSearchDelegate? - @objc var currentFilter: String = "" + var currentFilter: String = "" var previousFilter: String? var needUpdateLastSearchContacts = false @@ -35,36 +34,45 @@ final class MagicSearchSingleton: ObservableObject { @Published var lastSearch: [SearchResult] = [] private var limitSearchToLinphoneAccounts = true - - @Published var allContact = false - private var domainDefaultAccount = "" + + @Published var allContact = false + private var domainDefaultAccount = "" private init() { - domainDefaultAccount = coreContext.mCore.defaultAccount!.params!.domain! - - magicSearch = try? coreContext.mCore.createMagicSearch() - magicSearch.limitedSearch = false - - magicSearchDelegate = MagicSearchDelegateStub(onSearchResultsReceived: { (magicSearch: MagicSearch) in - self.needUpdateLastSearchContacts = true - self.lastSearch = magicSearch.lastSearch - }) - - magicSearch.addDelegate(delegate: magicSearchDelegate!) + coreContext.doOnCoreQueue{ core in + self.domainDefaultAccount = core.defaultAccount?.params?.domain ?? "" + + self.magicSearch = try? core.createMagicSearch() + self.magicSearch.limitedSearch = false + + self.magicSearch.publisher?.onSearchResultsReceived?.postOnMainQueue { (magicSearch: MagicSearch) in + self.needUpdateLastSearchContacts = true + self.lastSearch = magicSearch.lastSearch + } + } } func searchForContacts(sourceFlags: Int) { - if let oldFilter = previousFilter { - if oldFilter.count > currentFilter.count || oldFilter != currentFilter { - magicSearch.resetSearchCache() + coreContext.doOnCoreQueue{ core in + var needResetCache = false + + DispatchQueue.main.sync { + if let oldFilter = self.previousFilter { + if oldFilter.count > self.currentFilter.count || oldFilter != self.currentFilter { + needResetCache = true + } + } + self.previousFilter = self.currentFilter } + if needResetCache { + self.magicSearch.resetSearchCache() + } + + self.magicSearch.getContactsListAsync( + filter: self.currentFilter, + domain: self.allContact ? "" : self.domainDefaultAccount, + sourceFlags: sourceFlags, + aggregation: MagicSearch.Aggregation.Friend) } - previousFilter = currentFilter - - magicSearch.getContactsListAsync( - filter: currentFilter, - domain: allContact ? "" : domainDefaultAccount, - sourceFlags: sourceFlags, - aggregation: MagicSearch.Aggregation.Friend) } }