viewmodel based navigation as first class citzen for all platforms
Opened this issue ยท 13 comments
Our routing is something that should be overhauled in the future. It is far too prescriptive and platform-specific. ReactiveUI should really provide a general routing mechanism that cares not about how you manifest it in the view. All that logic should be written โ_once_โ (instead of per platform like we have now) and shared with simple shims to integrate at the UI level.
This is a great opportunity to look around and see how others are doing it. @rid00z has mentioned over beers he has suggestions from implementing this type of logic as part of his framework FreshMvvm.
Additionally MvvmCross has the notion of handling this behavior via Presenters
- http://gregshackles.com/presenters-in-mvvmcross-a-primer/
- http://gregshackles.com/presenters-in-mvvmcross-navigating-android-with-fragments/
@jcmm33's team has done a significant amount of work on-top of ReactiveUI and have implemented their own presenter solution. Maybe this is a chance to use this implementation as the golden standard and off-load the burden from his team of having to maintain their own.
Suggested leads for this topic are @jcmm33, @rid00z, @codemillmatt and of course @kentcb / @flagbug
Our approach in this area is something that has evolved over quite a while, but it is something that we are now quite happy with in principle, with the occasional tweak as to how we do things.
We started out by pretty much taking the MvvmCross approach namely providing a ViewModelType along with parameter values for the view model and presentation information.
It should however be noted at this point that we operate a convention whereby ViewModel constructors (for presented/navigated ones anyway) don't vary dependant upon the parameters/values required for that view model to work. e.g. if it were a view model showing a users details, then a specific users details wouldn't be provided in the constructor but instead the service/controller/manager capable of looking up a users details would. The actual specific user details would be provided through either view model initialisation (a topic on its own) or say through other method calls/properties on the view model.
e.g.
public class UserDetailsViewModel:RxViewModel
{
public UserDetailsViewModel(IUserController userController)
{
}
public void Present(Guid userId)
{
}
}
It should also be noted that by taking this approach serialization of view models for suspend/resume scenarios becomes very easy to do.
The MvvmCross details on navigation can be seen here in more detail https://github.com/MvvmCross/MvvmCross/wiki/ViewModel--to-ViewModel-navigation
As with MvvmCross we allow for custom presenters to be used instead of the standard ones.
Moving on from this, we started, as a pattern providing simplistic static methods on the appropriate viewmodels to create the presentation request instead of having view models littered with untyped parameters being provided e.g.
public class UserDetailsViewModel:RxViewModel
{
...
public static ViewModelPresentRequest CreateViewRequest(Guid userId)
{
...
}
The underlying presenter is still the same, its just how the parameters are constructed for the presenter.
One final change (we have been doing as of late) is injecting a fully typed presenter into the view models instead. So, instead of constructing a view model request we simply go things like:
ShowAll = ReactiveCommand.CreateAsyncObservable(_ => presenter.ShowSearchResultsFor( _searchInstance, _travelLimit));
Once again, all this presenter does is invoke the appropriate methods to create the view model request and pass it on, but it does provide a clean separation and allows for easy mocking up of presentation services.
Our intention has always been to make all of our work available on github once we are fully happy with it. This is still the case.
It should be noted that this is a highly opinionated framework/library in its approach and the 3rd party libraries used. Our approach has always been to improve delivery and code reliability, not provide some abstract framework which can be mixed and matched at will, so libraries like Autofac and AutoMapper are used heavily and our solution currently depends on these frameworks (especially Autofac) to function.
Warning, possibly dumb question from clueless newbie:
Does @thedillonb have some presenter stuff in CodeHub that is worth mentioning here?
Despite encountering advice from @paulcbetts about using "view first" on iOS/Android, I am investigating ways of using viewmodel based navigation with ReactiveUI for a Xamarin app.
One possibility is to use MvvmCross for the navigation stuff while using ReactiveUI for binding and commands and FRP goodness.
But studying the CodeHub code is, er, interesting.
His BaseViewModel class has a method called NavigateTo()
https://github.com/thedillonb/CodeHub/blob/master/CodeHub.Core/ViewModels/BaseViewModel.cs#L21
In the iOS code, I see stuff like this:
These things make me curious. But maybe that's because I've only been looking at ReactiveUI for a few days.
@ericsink, I tried to model CodeHub's VM first paradigm around delegation to the current view. If you've ever looked at other solutions, like MVVMCross, you'll notice that it uses VM first delegation to a common "presenter" which I took as inspiration but found it incredibly limiting since you end up with a big switch statement in a "presenter" class. I believe the more robust way is to simply delegate to the current view on how it wants to present it's child views - thats what you see at https://github.com/thedillonb/CodeHub/blob/master/CodeHub.iOS/ViewControllers/BaseViewController.cs#L49. Delegation to the current view means there's no big switch block and it ends up being extremely simple to test and utilize in other platforms.
That being said, it's just my simple, off-the-top-of-my-head solution which may not work for everyone, but happened to work pretty well in CodeHub.
Hey @ghuntley ๐,
Thank you for opening an issue. We will get back to you as soon as we can. Also, check out our Open Collective and consider backing us.
https://opencollective.com/reactiveui
PS.: We offer
priority
support for all backers. Don't forget to addpriority
label when you start backing us ๐
An advanced, composable, functional reactive model-view-viewmodel framework for all .NET platforms!
Hi all,
As a swear by Prism dev, I find that RxUI doesn't provide enough in terms of navigation and routing. I actually liked the way it worked in Prism with DI INavigationAware
and INavigationService
, but IScreen
and IRoutingViewModel
sounds good too.
Can we abstract them out into a separate non-Xamarin.Forms
dependent package, to allow for extension and implementation in other platforms?
Yeah that's the plan, the work is on-going to separate it out at the moment.
I actually liked the way it worked in Prism with DI INavigationAware and INavigationService, but IScreen and IRoutingViewModel sounds good too.
Can we abstract them out into a separate non-Xamarin.Forms dependent package, to allow for extension and implementation in other platforms?
Probably missing the context, by what do you mean by that @weitzhandler? Currently, IScreen
, RoutingState
and IRoutableViewModel
interfaces are located in the ReactiveUI core package, and are compatible not only with Xamarin.Forms, but also with UWP, WPF, Avalonia, etc. (RoutedViewHost
s implementations exist for almost every platform except Android and iOS)
@worldbeater I see now, I didn't know that. Will make more research on that. Thanks.
So what Sextant
does is just providing the XF NavigationPage
and VM registration?
It's a work in progress rewrite of the navigation stack inside RxUI, but the old navigation stack works as well until it's finished. Given it's very work in progress it's likely to change dramatically.
I'm gonna start off with IScreen
and IRoutableViewModel
. As new things will come in I'll move on.
BTW, I'm using Splat.DryIoc Can't afford to give up on DI sorry.
We added the DI containers to allow people to have a choice :) -- so both the traditional and sextant approach will interop with the registered splat DI container
Awesome then!