QuickBirdEng/XCoordinator

Initial or pre-used cordinators could not be dismissed

berkakkerman opened this issue · 4 comments

I am trying to implemented an app with sign in and sign out flow. Firstly, I have main coordinator(AppCoordinator) like below. AppCoordinator routes to AuthCoordinator first and AuthCoordinator has 3 views;

AuthRoute.login -> AuthRoute.setupName -> AuthRoute.addAccount

After a successful sign in, App routes to HomeCoordinator with a publish subject subscription.
HomeCoordinator is like TabBarCoordinator. In the Profile tab, a sign out button action is available.

HomeRoute

  • History
  • Payment
  • Profile Tab -> triggers HomeRoute. logout

After logout

.multiple(.dismissAll(), .presentFullScreen(self.authCoordinator, animation: .default))

Although I did .dismissAll() first, the old AuthCoordinator is opening. AuthCoordinator keeps its state. How can I re-create the AuthCoordinator and start it again from a new AuthCoordinator. This issue is happening for the HomeCoordinator.

AppCoordinator is created by a function from AppDelegate.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { setupCoordinator() return true }

AppCoordinator is instantiating with Swinject here.

private func setupCoordinator() { self.appCoordinator = appAssembler.resolver.resolve(AppCoordinator.self)! let router = self.appCoordinator.strongRouter router.setRoot(for: mainWindow) }

AppCoordinator with 3 routes.

class AppCoordinator: NavigationCoordinator<AppRoute> {

    private let homeCoordinator: HomeCoordinator
    private var authCoordinator: AuthCoordinator
    private let introCoordinator: IntroCoordinator
    
  
    init(homeCoordinator: HomeCoordinator,
         authCoordinator: AuthCoordinator,
         introCoordinator: IntroCoordinator ) {
        self.authCoordinator = authCoordinator        
        self.homeCoordinator = homeCoordinator
        self.introCoordinator = introCoordinator
        var initialRoute: AppRoute = .intro
        
        if userDefaultService.isShowIntro {
            if sessionService.isTokenValid {
                initialRoute = .home
            } else {
                initialRoute = .auth
            }
        }
        
        super.init(initialRoute: initialRoute)
        self.subscribeToSessionChanges()
    }
    
    // MARK: - Overrides
    override func prepareTransition(for route: AppRoute) -> NavigationTransition {
        
        switch route {
        case .auth:
            return .multiple(.dismissAll(), .presentFullScreen(self.authCoordinator, animation: .default))
        case .home:
            return .multiple(.dismissAll(), .presentFullScreen(self.homeCoordinator, animation: .default))
        case .intro:
            return .multiple(.dismissAll(), .presentFullScreen(self.introCoordinator, animation: .default))
        }
    }
private func subscribeToSessionChanges() {
       
       self.sessionService.didSignIn
           .subscribe(onNext: { [weak self] _ in
               self?.showHome()
           })
           .disposed(by: self.disposeBag)
       
       self.sessionService.didSignOut
           .subscribe(onNext: { [weak self] _ in
               self?.showAuth()
           })
           .disposed(by: self.disposeBag)
   }

enum AuthRoute: Route {
    case login
    case setupName(SetupNameModel)
    case setupAccount(AddAccountModel)
    case signOut
}
class AuthCoordinator: NavigationCoordinator<AuthRoute> {
    
    // MARK: - Stored properties
        
    // MARK: Initialization
    
    init(initialRoute: AuthRoute = .login) {
        super.init(initialRoute: initialRoute)
    }
    
    // MARK: Overrides
    
    override func prepareTransition(for route: AuthRoute) -> NavigationTransition {
        switch route {
        case .login:
            let viewController = ModuleBuilder<LoginVC>.buildModule(coordinator: unownedRouter)
            return .push(viewController)
            return .push(viewController)
        case .setupName(let model):
            let viewController = ModuleBuilder<SetupNameVC>.buildModule(context: model, coordinator: unownedRouter)
            return .push(viewController)
        case .setupAccount(let model):
            let viewController = ModuleBuilder<AddAccountVC>.buildModule(context: model, coordinator: unownedRouter)
            return .push(viewController)
        case .signOut:
            self.rootViewController.removeFromParent()
            return .multiple(.popToRoot(), .dismissAll(), .dismissToRoot(), .dismiss())
        }
    }
}
class AuthCoordinator: NavigationCoordinator<AuthRoute> {
    
    // MARK: - Stored properties
        
    // MARK: Initialization
    
    init(initialRoute: AuthRoute = .login) {
        super.init(initialRoute: initialRoute)
    }
    
    // MARK: Overrides
    
    override func prepareTransition(for route: AuthRoute) -> NavigationTransition {
        switch route {
        case .login:
            let viewController = ModuleBuilder<LoginVC>.buildModule(coordinator: unownedRouter)
            return .push(viewController)
            return .push(viewController)
        case .setupName(let model):
            let viewController = ModuleBuilder<SetupNameVC>.buildModule(context: model, coordinator: unownedRouter)
            return .push(viewController)
        case .setupAccount(let model):
            let viewController = ModuleBuilder<AddAccountVC>.buildModule(context: model, coordinator: unownedRouter)
            return .push(viewController)
        case .signOut:
            self.rootViewController.removeFromParent()
            return .multiple(.popToRoot(), .dismissAll(), .dismissToRoot(), .dismiss())
        }
    }
}
class HomeCoordinator: TabBarCoordinator<HomeRoute> {
    
    // MARK: Stored properties
    
    private let historyRouter: StrongRouter<HistoryRoute>
    private let paymentRouter: StrongRouter<PaymentRoute>
    private let profileRouter: StrongRouter<ProfileRoute>
    
    // MARK: Initialization
    
    init(historyCoordinator: HistoryCoordinator,
         paymentCoordinator: PaymentCoordinator,
         profileCoordinator: ProfileCoordinator) {
        
        self.historyRouter = historyCoordinator.strongRouter
        self.paymentRouter = paymentCoordinator.strongRouter
        self.profileRouter = profileCoordinator.strongRouter
        
        let homeVC = ModuleBuilder<HomeVC>.buildModule()
        super.init(rootViewController: homeVC, tabs: [historyRouter, paymentRouter, profileRouter], select: paymentRouter)
    }
    
    // MARK: Overrides
    
    override func prepareTransition(for route: HomeRoute) -> TabBarTransition {
        switch route {
        case .history:
            return .select(historyRouter)
        case .payment:
            return .select(paymentRouter)
        case .profile:
            return .select(profileRouter)
        }
    }
}
enum ProfileRoute: Route {
    case logout
}
class ProfileCoordinator: NavigationCoordinator<ProfileRoute> {
    
    // MARK: Initialization
    
    let authCoordinator: AuthCoordinator
    
    init(authCoordinator: AuthCoordinator) {
        self.authCoordinator = authCoordinator
        super.init(initialRoute: .profile)
    }
    
    // MARK: Overrides    
    override func prepareTransition(for route: ProfileRoute) -> NavigationTransition {
        switch route {
        case .logout:
            return .multiple(.dismissAll(), .dismiss())
        }
    }
}

@berkakkerman any solution to this ?

@mathemandy Unfortunately, not yet :(

Hey,

each coordinator has a rootViewController (e.g. a UINavigationController in the case of a NavigationCoordinator), which it holds strongly. To make sure, that each coordinator is cleaned, you will have to trigger a route on the AuthCoordinator that is removing the existing state - or: can you simply create a new instance instead?

@pauljohanneskraft Thank you. It works like a charm right now.