diff --git a/Riot/Modules/Authentication/AuthenticationCoordinator.swift b/Riot/Modules/Authentication/AuthenticationCoordinator.swift index 2c3a38beb..a310cdb1a 100644 --- a/Riot/Modules/Authentication/AuthenticationCoordinator.swift +++ b/Riot/Modules/Authentication/AuthenticationCoordinator.swift @@ -19,17 +19,6 @@ import Foundation import UIKit -/// AuthenticationCoordinator input parameters -struct AuthenticationCoordinatorParameters { - /// The initial type of authentication to be shown - let authenticationType: MXKAuthenticationType - /// The registration parameters. - let externalRegistrationParameters: [AnyHashable: Any]? - /// The credentials to use after a soft logout has taken place. - let softLogoutCredentials: MXCredentials? -} - - /// A coordinator that handles authentication, verification and setting a PIN. final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtocol { @@ -37,7 +26,6 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc // MARK: Private - private let parameters: AuthenticationCoordinatorParameters private let authenticationViewController: AuthenticationViewController // MARK: Public @@ -48,12 +36,14 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc // MARK: - Setup - init(parameters: AuthenticationCoordinatorParameters) { - self.parameters = parameters - + override init() { let authenticationViewController = AuthenticationViewController() self.authenticationViewController = authenticationViewController + // Preload the view as this can a second and lock up the UI at presentation. + // The coordinator is initialised early in the onboarding flow to take advantage of this. + authenticationViewController.loadViewIfNeeded() + super.init() } @@ -62,43 +52,31 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc func start() { // Listen to the end of the authentication flow authenticationViewController.authVCDelegate = self - - // Set authType first as registration parameters or soft logout credentials - // may update this afterwards to handle those use cases. - authenticationViewController.authType = parameters.authenticationType - if let externalRegistrationParameters = parameters.externalRegistrationParameters { - authenticationViewController.externalRegistrationParameters = externalRegistrationParameters - } - if let softLogoutCredentials = parameters.softLogoutCredentials { - authenticationViewController.softLogoutCredentials = softLogoutCredentials - } } func toPresentable() -> UIViewController { return self.authenticationViewController } - /// Force a registration process based on a predefined set of parameters from a server provisioning link. - /// For more information see `AuthenticationViewController.externalRegistrationParameters`. + func update(authenticationType: MXKAuthenticationType) { + authenticationViewController.authType = authenticationType + } + func update(externalRegistrationParameters: [AnyHashable: Any]) { authenticationViewController.externalRegistrationParameters = externalRegistrationParameters } - /// Set up the authentication screen with the specified homeserver and/or identity server. + func update(softLogoutCredentials: MXCredentials) { + authenticationViewController.softLogoutCredentials = softLogoutCredentials + } + func updateHomeserver(_ homeserver: String?, andIdentityServer identityServer: String?) { authenticationViewController.showCustomHomeserver(homeserver, andIdentityServer: identityServer) } - /// When SSO login succeeded, when SFSafariViewController is used, continue login with success parameters. func continueSSOLogin(withToken loginToken: String, transactionID: String) -> Bool { authenticationViewController.continueSSOLogin(withToken: loginToken, txnId: transactionID) } - - /// Preload `AuthenticationViewController` from it's xib file to avoid locking up the UI when before presentation. - static func preload() { - let authenticationViewController = AuthenticationViewController() - authenticationViewController.loadViewIfNeeded() - } } // MARK: - AuthenticationViewControllerDelegate diff --git a/Riot/Modules/Authentication/AuthenticationCoordinatorProtocol.swift b/Riot/Modules/Authentication/AuthenticationCoordinatorProtocol.swift index 7409fc2af..7037f9d1f 100644 --- a/Riot/Modules/Authentication/AuthenticationCoordinatorProtocol.swift +++ b/Riot/Modules/Authentication/AuthenticationCoordinatorProtocol.swift @@ -22,10 +22,16 @@ import Foundation protocol AuthenticationCoordinatorProtocol: Coordinator, Presentable { var completion: (() -> Void)? { get set } + /// Update the screen to display registration or login. + func update(authenticationType: MXKAuthenticationType) + /// Force a registration process based on a predefined set of parameters from a server provisioning link. /// For more information see `AuthenticationViewController.externalRegistrationParameters`. func update(externalRegistrationParameters: [AnyHashable: Any]) + /// Update the screen to use any credentials to use after a soft logout has taken place. + func update(softLogoutCredentials: MXCredentials) + /// Set up the authentication screen with the specified homeserver and/or identity server. func updateHomeserver(_ homeserver: String?, andIdentityServer identityServer: String?) diff --git a/Riot/Modules/Onboarding/OnboardingCoordinator.swift b/Riot/Modules/Onboarding/OnboardingCoordinator.swift index 16056608a..8a6dcc7c4 100644 --- a/Riot/Modules/Onboarding/OnboardingCoordinator.swift +++ b/Riot/Modules/Onboarding/OnboardingCoordinator.swift @@ -50,11 +50,16 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol { /// A custom identity server to be used once logged in. private var customIdentityServer: String? + // MARK: Navigation State private var navigationRouter: NavigationRouterType { parameters.router } + // Keep a strong ref as we need to init authVC early to preload its view (it is *really* slow to do in realtime) + private var authenticationCoordinator: AuthenticationCoordinatorProtocol = AuthenticationCoordinator() + private var isShowingAuthentication = false + + // MARK: Screen results private var splashScreenResult: OnboardingSplashScreenViewModelResult? - private weak var authenticationCoordinator: AuthenticationCoordinatorProtocol? // MARK: Public @@ -75,7 +80,6 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol { // TODO: Manage a separate flow for soft logout that just uses AuthenticationCoordinator if #available(iOS 14.0, *), parameters.softLogoutCredentials == nil, BuildSettings.authScreenShowRegister { showSplashScreen() - preloadAuthentication() } else { showAuthenticationScreen() } @@ -89,19 +93,19 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol { /// For more information see `AuthenticationViewController.externalRegistrationParameters`. func update(externalRegistrationParameters: [AnyHashable: Any]) { self.externalRegistrationParameters = externalRegistrationParameters - authenticationCoordinator?.update(externalRegistrationParameters: externalRegistrationParameters) + authenticationCoordinator.update(externalRegistrationParameters: externalRegistrationParameters) } /// Set up the authentication screen with the specified homeserver and/or identity server. func updateHomeserver(_ homeserver: String?, andIdentityServer identityServer: String?) { self.customHomeserver = homeserver self.customIdentityServer = identityServer - authenticationCoordinator?.updateHomeserver(homeserver, andIdentityServer: identityServer) + authenticationCoordinator.updateHomeserver(homeserver, andIdentityServer: identityServer) } /// When SSO login succeeded, when SFSafariViewController is used, continue login with success parameters. func continueSSOLogin(withToken loginToken: String, transactionID: String) -> Bool { - guard let authenticationCoordinator = authenticationCoordinator else { return false } + guard isShowingAuthentication else { return false } return authenticationCoordinator.continueSSOLogin(withToken: loginToken, transactionID: transactionID) } @@ -128,27 +132,32 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol { showAuthenticationScreen() } - /// Preload the authentication view controller to avoid a delay during its presentation - private func preloadAuthentication() { - AuthenticationCoordinator.preload() - } - /// Show the authentication screen. Any parameters that have been set in previous screens are be applied. private func showAuthenticationScreen() { - guard authenticationCoordinator == nil else { return } + guard !isShowingAuthentication else { return } MXLog.debug("[OnboardingCoordinator] showAuthenticationScreen") - let parameters = AuthenticationCoordinatorParameters(authenticationType: splashScreenResult == .register ? MXKAuthenticationTypeRegister : MXKAuthenticationTypeLogin, - externalRegistrationParameters: externalRegistrationParameters, - softLogoutCredentials: parameters.softLogoutCredentials) - - let coordinator = AuthenticationCoordinator(parameters: parameters) + let coordinator = authenticationCoordinator coordinator.completion = { [weak self, weak coordinator] in guard let self = self, let coordinator = coordinator else { return } self.authenticationCoordinatorDidComplete(coordinator) } + // Due to needing to preload the authVC, this breaks the Coordinator init/start pattern. + // This can be re-assessed once we re-write a native flow for authentication. + + // Set authType first as registration parameters or soft logout credentials will modify this. + let mxkAuthenticationType = splashScreenResult == .register ? MXKAuthenticationTypeRegister : MXKAuthenticationTypeLogin + coordinator.update(authenticationType: mxkAuthenticationType) + + if let externalRegistrationParameters = externalRegistrationParameters { + coordinator.update(externalRegistrationParameters: externalRegistrationParameters) + } + if let softLogoutCredentials = parameters.softLogoutCredentials { + coordinator.update(softLogoutCredentials: softLogoutCredentials) + } + coordinator.start() add(childCoordinator: coordinator) authenticationCoordinator = coordinator @@ -162,12 +171,15 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol { } else { self.navigationRouter.push(coordinator, animated: true) { [weak self] in self?.remove(childCoordinator: coordinator) + self?.isShowingAuthentication = false } } + isShowingAuthentication = true } /// Displays the next view in the flow after the authentication screen. - private func authenticationCoordinatorDidComplete(_ coordinator: AuthenticationCoordinator) { + private func authenticationCoordinatorDidComplete(_ coordinator: AuthenticationCoordinatorProtocol) { completion?() + isShowingAuthentication = false } } diff --git a/RiotSwiftUI/Modules/Onboarding/Common/OnboardingConstants.swift b/RiotSwiftUI/Modules/Onboarding/Common/OnboardingConstants.swift index 4dc6c9c6e..eed2cb042 100644 --- a/RiotSwiftUI/Modules/Onboarding/Common/OnboardingConstants.swift +++ b/RiotSwiftUI/Modules/Onboarding/Common/OnboardingConstants.swift @@ -14,7 +14,7 @@ // limitations under the License. // -import Foundation +import CoreGraphics /// Constants used across the entire onboarding flow. struct OnboardingConstants {