ekazaev/route-composer

Could you provide a MVVM+RxSwift example?

lts1610 opened this issue · 8 comments

Could you provide a MVVM+RxSwift example?

You need to describe me what exactly you want to see in this example? From my understanding route-composer sits aside from the app architecture. If you need to navigate somewhere, you call the router. And you can wrap it in any architecture you want. And call your method/class on top of it. If you need to make some signal at the end of navigation - call it in the completion method of the Router.

#if canImport(RxSwift)
import RxSwift

extension Router {

    public var rx: Reactive<Self> {
        return Reactive(self)
    }

}

extension Reactive where Base: Router & AnyObject {

    func navigate<ViewController, Context>(to step: DestinationStep<ViewController, Context>, with context: Context, animated: Bool) where ViewController: UIViewController  -> Observable<Void> {
        return Observable.create { [weak base] observer -> Disposable in
            guard let base = base else {
                observer.onCompleted()
                return Disposables.create()
            }
            do {
                try navigate(to: step, with: context, animated: animated, completion: { result in
                    guard result.isSuccessfull else {
                         observer.onError(error)
                         return
                    }
                    observer.onNext(())
                    observer.onCompleted()
                })
            } catch let error {
                observer.onError(error)
            }
            return Disposables.create()
        }
    }

}

#endif

I assume it should be something like this. But in general there are thousands of way to implement that. As there is thousands of ways to deal with MVVM. It is an architecture not a pattern. I am not 100% sure what outcome you expect to get in RxSwift. I believe that navigation can be triggered by the view model, but there should be some kind of wireframe object that sticks that whole app together and exposes the navigation methods to the application modules to provide the navigation between them. So basically sits outside of the MVVM architecture.

I seeing that each ViewController have either a Context or not. In MVVM, could we consider it as a ViewModel?

And I have a scenario:

  1. The ViewControllerA have a ViewModelA. The ViewModelA have a reload PublishSubject.
  2. The ViewControllerB have a ViewModelB. The ViewModelB have a result PublishSubject.
  3. In the ViewControllerA, a user taps on a next button, the system navigates to ViewControllerB
  4. The user does something on ViewControllerB and the user taps on a done button, the system navigates to ViewControllerA
  5. The ViewControllerA need to reload with the result from the ViewModelB of the ViewControllerB by binding the result PublishSubject to the reload PublishSubject.

@ekazaev Where to place binding the result of ViewModelB to the reload of ViewModelA?

No, context is some data that you have to pass from one screen to another. While view model is something that represents the whole screen itself.
Lets say you have a product list, and you need to navigate to some specific product screen when user selects it in the list. It mean that the context of the product screen is a product id type. The String type for example.

Okay, I got it. Please help me to resolve the scenario as above.

I am not sure about your particular view model implementation, but you need to make your ViewModelA be aware of the actions ViewModelB. It can be implemented in a lot of ways.
One of them is to pass a subscriber to the events of the ViewModelB with the Context. In the example i gave you with authorisation - i pass delegate object as the Context. Which is basically a subscriber in RxSwift.

But it also depends on what exactly you are doing. It might not be the best option in your case.

What i want to say, that the thing you are trying to solve is not a navigation issue. Done button let's say hides ViewControllerB presented modally. Basically it does dismissViewController(amimated:) call. Modal view controller can be hidden with the swipe down as well (of course if you'll implement it). So basically here we destroy a view controller, and as a result of this destruction we appear on the ViewControllerA which needs to update its ViewModelA.
So it is not exactly an issue to solve with the router or RouteComposer. RouteComposer can be part of the solution, but it might be not if you dismiss modal view controller with the swipe or without using router at all.
This solution should sit somewhere in your business logic. Delegation pattern, RxSwift and so on are the instruments you can use.

You may look on the similar example with the UINavigationController, You have there 2 view controller A and B, and when user taps Back button there you need to update view controller A. Also user can swipe back to the A view controller. Neither RouteComposer nor ViewModlelB are involved in this scenario. May be in this case would be better to update view controller A on every viewWillAppear call instead of building the subscriber chain as it wont work in this case. A lot of cases - a lot of possible solutions.

@ekazaev
I am considering applying it to my project. If have any another issue, I'll feedback for you.
Thank you so much.