Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Starting AppCoordinator again #3

Open
cojoj opened this issue Oct 29, 2017 · 5 comments
Open

Starting AppCoordinator again #3

cojoj opened this issue Oct 29, 2017 · 5 comments

Comments

@cojoj
Copy link

cojoj commented Oct 29, 2017

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
Copy link
Collaborator

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
Copy link
Author

cojoj commented Nov 6, 2017

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
Copy link

farzadshbfn commented Mar 19, 2018

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

@chrsp
Copy link

chrsp commented Dec 10, 2020

@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
Copy link

chrsp commented Dec 11, 2020

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants