uptechteam / Coordinator-MVVM-Rx-Example

Example of MVVM-C architecture implemented with RxSwift
562 stars 100 forks source link

Starting AppCoordinator again #3

Open cojoj opened 6 years ago

cojoj commented 6 years ago

Thanks for the idea and implementation, this is a great resource on how to crate a nice, maintainable and also very flexible all architecture!

I’m struggling right now with “sign out” functionality in our application. I have two coordinators: AuthCoordinator And MainCoordinator. In my AppCoordinator I start with AuthCoordinator Which later can redirect me to MainCoordinator, if authorization succeeds. The problem is that one of the ViewControllers In my MainCoordinator Has “sign out” button which should start AppCoordinator From scratch. I have no clue how to achieve this, because I want to avoid so deep dependencies between different coordinators etc. Do you have some sort of solution for this kind of behavior?

arthur-here commented 6 years ago

Hi @cojoj. Thank you so much for the feedback! That's a really good question, I was waiting someone to ask 😅

I'll describe the approach we are using in our apps. We manage the auth state in some singleton service AuthService which has a property didLogout: Observable<Void>. The coordinators can observe that property and act appropriately. For example:

class AppCoordinator: BaseCoordinator<Void> {

  override func start() -> Observable<Void> {
    showAuth()

    // The app coordinator will never finish
    return .never()
  }

  // Recursive auth function that will restart the AuthCoorinator after completion.
  private func showAuth() {
    let authCoordinator = AuthCoordinator(window: window, container: container)
    return coordinate(to: authCoordinator)
      .subscribe(onNext: { [weak self] in 
        self?.window?.rootViewController = nil
        self?.showAuth() 
      })
      .disposed(by: disposeBag)
  }
}

class AuthCoordinator: BaseCoordinator<Void> {
  override func start() -> Observable<Void> {
    // Present Auth ViewController and stuff
    // ...

    let authService: AuthService = try! container.resolve()

    // Auth Coordinator observes `didLogout` property and finishes only when user has logged out
    return authService.didLogout.take(1)
  }
}

Personally, I don't like that approach too much, because of the shared AuthService singleton and that strange recursive call. We are searching for the better way to handle that use case and are open for the suggestions ❤️

Hope this answers your question, let me know if you have any problems with it.

cojoj commented 6 years ago

Ok, I see... I guess we somehow swim in the same pool with this.

What I did so far is skip this recursive thing (though I completely skipped AuthService and just had PublishSubject in AppCoordinator) and thought it complicates stuff. Instead here's bow my AppCoordinator looks like:

final class AppCoordinator: BaseCoordinator<Void> {
    // MARK: Private properties
    private let router: Router

    init(router: Router) {
        self.router = router
    }

    override func start() -> Observable<Void> {
        return runAuthFlow()
    }

    // MARK: Private methods
    private func runAuthFlow() -> Observable<Void> {
        let coordinator = AuthFlowCoordinator(router: router)
        return coordinate(to: coordinator)
    }
}

And of course, AuthFlowCoordinator handles everything and when appropriate (user signed in correctly) it coordinates to MainFlowCoordinator. And following the very same pattern - when user goes to Profile screen and taps on sign out button I simply call coordinate(to: AuthFlowCoordinator(router: router)) and the story begins...

One thing that really bothers me is that every start method returns Observable<Void> instead of something more meaningful, but maybe it's just for now and in the future it'll change.


Also, one more question (not really related, but...), have you implemented this Coordinator pattern without RxSwift? FRP is great for this stuff, but having something like this as a 3rd party dependency with support for regular code and FRP would be a great thing! Coordination is always a big PITA in bigger and not linear iOS projects and having library supporting this stuff would be awesome!

farzadshbfn commented 6 years ago

How about, we use ChainOfResponsibility here? Sending logout event back and forth between children and parents? (in this case only to parents)

chrsp commented 3 years ago

@arthur-here did you find a better solution for this problem? I'm also facing the same issue, and my solution is very similar to yours. I'm also not happy with this approach :(

chrsp commented 3 years ago

I created this project as an example, to make easier to reach to a solution for this problem. It describes two common scenarios in which the Coordinators with Rx seems to have some flaws. Have anyone here already found some satisfactory solution for those problems?