Wow! It’s been a while, but we have a juicy new release for you. We had to take some time to heal internally, to train and mentor the future generations of maintainers which are going to carry us forward. Additionally the .NET ecosystem changed underneath us, and we didn’t have continuous integration setup. Let this be a lesson to anyone considering operating an open-source project that targets nine platforms. Have a roadmap and succession plan in place and never allow long-running branches, ever. Implement continuous integration and mature your release processes to the point where cutting a new release is as simple as pressing a button.
So what is ReactiveUI?
ReactiveUI is inspired by functional reactive programming and is the father of the ReactiveCocoa (Cocoa/Swift) framework. Internally we debate whether we are or are not a framework, as at its core the project is essentially a bunch of extension methods for the Reactive Extensions.
ReactiveUI was started seven years ago and is now old enough to attend grade school but unlike a teenager is extremely stable. ReactiveUI has matured over the years into a solid and fine choice for building your next application. Additionally, because the implementation is unopinionated migration from another framework to ReactiveUI is incredibly easy.. You can slide it in on a single ViewModel and then migrate as you become more comfortable. Avoid those costly rewrites.
When reading the code, you’ll find that ReactiveUI is rather unopinionated in the implementation, but we have always held some beliefs which have been the basis and foundation of the project.
We believe that code is communication between people, that also happens to run on a computer. If you optimise for humans, then over a long time your project will end up better. Software should be understandable by other people; that is super important.
We believe that only the power of the Reactive Extensions allows you to express the idea around a feature in one readable place.
Think about your typical user interface? It’s a mutable bag of sadness with code all over the place. Instead of telling a computer how to do its job, why not define what the computers job is and get out of its way? If that sounds odd, let us re-introduce you to Microsoft Excel.
Instead of doing the typical ViewModel isLoading = true/false mutable dance by toggling it on and off in different branches of your code. Why not express the condition in a single place using a Microsoft Excel expression - =SUM(A1: B2)?
Still not convinced?
Maybe you need to watch this insanely smart, and eccentric guy in a tie-dye t-shirt do maths on a whiteboard:
Async/await is the zombie plague. Liberate your codebase today.
ReactiveUI is used in production at GitHub, Slack, Microsoft and is supported by consultants from different companies from all around the world. It’s used at our clients, we can’t name names specifically, but we encourage our community to showcase where and how they have used ReactiveUI in their applications, some members have even gone as far as open-sourcing their app and sharing their entire codebase. You are of course under no obligation to share these insights (or code) with us but it is greatly appreciated by the project maintainers, and you’ll usually get a retweet out of it.
Where are the examples?
We are working on it; this release was for us. Next release is for you. For now:
Rx is hard
No, it’s not. Learning Rx is one of the best things you can do to improve yourself as a software engineer. Unit testing was hard, so was dependency injection at first. The principals you learn during your journey will forever change you and best of all the knowledge is implementation and language agnostic. We have designed ReactiveUI so you can slowly transition from an async/await codebase at a pace that feels comfortable to you.
ReactiveCommand is completely rewritten again (sorry).
IReactiveCommandshould be replaced with
ReactiveCommand, possibly with type information (see below).
CreateXxxmethods, including synchronous commands (i.e. those created with
Create). So rather than calling
Createand then subscribing, you call
Createand pass in your execution logic right then and there.
scheduler) are optional.
CreateAsyncObservableis now called
CreateAsyncTaskis now called
objectand cast it. Instead, you explicitly specify the parameter type when creating the command (of course, you can still choose
TParamif that makes sense, perhaps as an intermediary migration step).
ICommandis now implemented explicitly. As a result:
ReactiveCommandis reactive (it returns
IObservable<TResult>). It is therefore lazy and won’t do anything unless something subscribes to it.
CanExecuteObservableis now called
IsExecutingare now behavioral. That is, they will always provide the current value to new subscribers.
RoutingStatehas been updated to use the new implementation. Consequently, any use of its commands will be affected per the above.
ToCommandextension method has been removed. This was a simple convenience to take an
IObservable<bool>and use it as the
canExecutepipeline for a new command. If you’re using
ToCommand, you can just replace it with a call to one of the creation methods on
var canExecute = ...; var someCommand = ReactiveCommand.Create(canExecute); someCommand.Subscribe(x => /* execution logic */); var someAsyncCommand1 = ReactiveCommand.CreateAsyncObservable(canExecute, someObservableMethod); var someAsyncCommand2 = ReactiveCommand.CreateAsyncTask(canExecute, someTaskMethod); someCommand.Execute();
var canExecute = ...; var someCommand = ReactiveCommand.Create(() => /* execution logic */); var someAsyncCommand1 = ReactiveCommand.CreateFromObservable(someObservableMethod, canExecute); var someAsyncCommand2 = ReactiveCommand.CreateFromTask(someTaskMethod, canExecute); someCommand.Execute().Subscribe();
For more details, please see the extensive documentation on this topic.
Note To enable you to ease into the migration, all previous types are available under the
ReactiveUI.Legacynamespace. Note, however, that there is no legacy version of
RoutingState, so any code you have that interacts with its command may require minor updates.
UserError has been generalized and re-imagined. We call it interactions, and we think you’ll like it. We did this in part because people were feeling icky using
UserError for non-error scenarios. Basically, we realized that people need a general mechanism via which a view model can ask a question, and wait for the answer. It doesn’t have to be an error - we’re not that pessimistic! You could be asking to confirm a file deletion, or maybe how the weather is out there in the analog world.
UserError to the interactions infrastructure is not really a case of one-for-one substitution. But here are some tips to get you started:
RegisterHandlermethods on the interaction exposed by the view model.
Handleon the interaction, passing in an input value.
Note To enable you to ease into the migration, all previous types are available under the
In previous ReactiveUI versions,
ToProperty was lazy. That is, it would have no effect unless something was “pulling” on the target property. This was for performance reasons, as you may have properties that are expensive to resolve, but only used in specific scenarios.
Whilst this was good for performance, it was often confusing and contrary to expectations. Therefore,
ToProperty is no longer lazy - it immediately subscribes to ensure the property’s value reflects the given observable pipeline. However, the original lazy behavior can be obtained by passing in
true to the
A mountain of effort has gone into automating ReactiveUI’s build and deployment infrastructure (thanks @ghuntley!). Using tools such as Cake, AppVeyor, and gitversion, we now have a very compelling automation story. This is no mean feat, especially considering the vast array of platforms on which ReactiveUI runs, and must therefore be built.
The end goal here is to get you, the community, new versions of ReactiveUI more rapidly and seamlessly.
As part of this release we had 113 issues closed.
WhenAnyObservableoverloads that combine latest via a given selector
UnhandledErrorExceptionwhen a thrown exception goes unhandled (rather than just
InvokeCommandto respect the command’s execution window
WhenActivatedoverload that takes a
CompositeDisposable, as well as a
ToPropertyto work with indexers
ToPropertyis no longer lazy by default
Bindoverloads that convert via
BindTonow throws a helpful exception when attempting to bind to
WhenNavigatedToObservablemethod, telling you whenever a given view model is on top of the navigation stack
ObservableAsPropertyHelperprovides the initial value immediately
fallbackValuefrom binding methods
ReactiveUserControlto relevant XAML platforms
XmlnsDefinitionAttributefor those XAML platforms that support it
ViewModelViewHostfor XAML platforms
Windows Phone, Store and Universal
ISubjectis returned as
ControlFetcherMixinfor the Android Support library
BindableProperty.Create, since the generic overloads are deprecated
ViewModelViewHostif the view is of the wrong type
ContentViewas base class for
ViewModelViewHostbug where collection is modified whilst being enumerated
ViewModelViewHostwith Xamarin Forms
ViewWillAppearfor iOS activation (not
CommonReactiveSourceto be reactive and fix numerous bugs
ViewModelViewHostan actual view controller
CommonReactiveSourcerace condition by ensuring that batched changes result in
ViewAttributesto Xamarin.Mac projects
You can download this release from nuget.org