We want to replace ReactiveUI by another MVVM toolkit. The candidate for such move is the library called CommunityToolkit.Mvvm AKA Microsoft.Toolkit.Mvvm. This text will use “CTM” for short.
The library has been evaluated, focusing on features that can be useful for Wasabi Wallet.
The main features of the library have been checked to see the viability of a transition from ReactiveUI:
- Source generation. This library has attributes to help users avoid adding repetitive code that usually clutters ViewModels.
- Optional base class for observable classes. Multiple inheritance can be simulated using attributes like
[INotifyPropertyChanged]
. - Messenger pattern.
- Support for IoC.
- Validation.
- Commands (including async commands).
- Mechanism to expose
Tasks
as properties.
CTM includes the [ObservableProperty]
and [RelayCommand]
attributes to simplify the code.
- ObservablePropertyAttribute does exactly what the existing AutoNotifyAttribute.
- RelayCommandAttribute is an attribute that can be added to a method. It will generate a command that invokes that method when the command is executed. There’s a property in the attribute to specify a Boolean property or method which controls executability. It also exposes options to control whether concurrent execution is allowed or not.
In order to make it easier to adapt existing code to CTM, it allows users to keep their current inheritance untouched thanks to source generation. Applying an attribute to a class will generate the appropriate members to, for example, implement INotifyPropertyChanged
.
This is important for us, since we aren’t removing RxUI’s ReactiveObject as base class for ViewModels, we can mix RxUI / CTM without breaking the current behavior.
This is built-in in RxUI. We don’t use it.
Built-in in RxUI. We don’t use it.
We have a validation system. Not evaluated as part of this assessment.
CTM supports the creation of both sync and asynchronous commands.
CTM use a wrapper class that can expose Tasks as properties. We don’t use it.
The features that we currently use have been checked for viability and these are the results:
- Source generation works perfectly to observe properties. The migration from
AutoNotify
toObservableProperty
should be trivial. However, it might be necessary to changeViewModelBase
to accommodateReactiveObject
with the code that is autogenerated by CTM. - We could the
RelayCommand
annotation to generate most of the simple commands. More complex commands are rare but would force us to keep using ReactiveCommand from RxUI. - Validation hasn’t been assessed. We might consider migrating this part if is CTM’s validation is convenient.
This assessment has been done using the latest preview of CommunityToolkit.Mvvm: 8.0.0 Preview 4. The latest stable version (7.x) doesn’t support code generation for generic classes, as showed in CommunityToolkit/WindowsCommunityToolkit#4600
There are 3 different aspects that have to be taken into account:
CTM is a very light MVVM framework that can cover the most common usage scenarios. We use reactive programming extensively in all our ViewModels and our Views. However, it hasn’t been designed to fit with reactive programming. This means that there’s no easy option to completely replace ReactiveUI. In fact, replacing it would be a bad move, since reactive programming has been helping us make our code more declarative and understandable. Doing the same with regular imperative programming has been proven error prone. If we replace RxUI by another framework, this other framework should offer us a simple way to convert regular properties to observables and vice versa. The same applies to Commands.
This framework offers the code generation features we already have. It works as expected and might allow us to remove our own code generation classes.
- A little inconvenience is that it’s not possible to specify the visibility of the setters in the generated properties, but that shouldn’t be a problem. However, we should take care because migration can be a bit more time consuming due to this.
This library looks like a basic MVVM framework with a low learning curve, and it seems it will continue to be so in the future.
- The library is quite new and we still cannot foresee which features will come in the future, but it's unlikely that it will gain compatibility with
System.Reactive
or something similar. - Something to take into account is that CTM is maintained by the community, usually people from Microsoft of related to the .NET Foundation, with all the consequences.
This is a list of steps that can be done with little effort and very low risk:
- Introduce CommunityToolkit.Mvvm 8.0, preferably when it’s been released.
- Replace
[AutoNotify]
by[ObservableProperty]
. - Review each
ReactiveCommand
in the solution. Simple ones, that are most of them, should be easily replaceable by the[RelayCommand]
annotation. - Remove existing code generation classes related with [AutoNotify].
- Keep ReactiveUI and CTM coexisting.
We need to review the code in our ViewModels.
Subscribe
methods shouldn’t contain business logic. Most of times it should be empty:.Subscribe();
- Simplify reactive pipelines:
Select(_ => Unit.Default)
can be converted toToSignal()
- Remove all calls to
ObserveOn
unless it’s necessary. - Bind to Observables when it’s possible. Avalonia can bind directly to them:
Eg.
{Binding IsVisible^}
whereIsVisible
isIObservable<bool>
This will allow us to avoid ObservableAsPropertyHelper.