Let's start with a quick recap of the reasons I am writing this post.
I find most examples on the web regarding how to write MVVM apps at fault when it comes to choosing where to place the logic about the flow and behaviour of the app - almost all logic is stuffed into the view models. The Model in MVVM is limited to service/repository wrappers and their DTOs. I think we can do better, I think the model has to be much richer and the view models much smaller.
Then I found Redux and I knew exactly what I needed to do, so I ported it to .NET and its name is Reducto. I introduced it in a previous post and there is a quick refresher on that in a following section, but for a more detailed overview, please head over here.
Agenda for this post
In this post the rubber meets the road - Reducto orchestrating your Xamarin Forms app. I am going to focus on a very small part of an app - logging in a user. I think it is sufficiently simple and common and will allow me to focus on the technology necessary for building the feature.
There are some small pieces of glue that I might not talk about here but they are rather simple and live under the
Infrastructure folder in the sample project where all material here is taken from. Although the example is with Xamarin Forms, everything should be applicable to other implementations of MVVM too.
Reducto is a keeper of the state for your app. It helps to organize the logic that changes that state.
A quick refresher on the core concepts in Reducto:
- Action - an object which describes what has happened - LoggedIn, SignedOut, etc. The object contains all the information relevant to the action - username, password, status, etc. Usually there are many actions in an app.
- Reducer - a side-effect free function that receives the current state of your app and an action. If the reducer does not know how to handle the action it should return the state as is. If the reducer can handle the action it 1.) makes a copy of the state 2.) it modifies it in response to the action and 3.) returns the copy.
- Store - it is an object that contains your app's state. It also has a reducer. We dispatch an action to the store which hands it to the reducer together with the current app state and then uses the return value of the reducer as the new state of the app. There is only one store in your app. It's created when your app starts and gets destroyed when your app quits. Your MVVM view models can subscribe to be notified when the state changes so they can update themselves accordingly.
- Async action - a function that may have side effects. This is where you talk to your database, call a web service, navigate to a view model, etc. Async actions can also dispatch actions (as described above). To execute an async action it needs to be dispatched to the store.
- Middleware - I will leave this part out for now
Dispatching an action to the store is the only way to change its state.
Dispatching an async action cannot change the state but it can dispatch actions(more about that in a few paragraphs) which in turn can change the state.
Let's jump right in and show you the action of logging a user in. It has to be async action since it needs to talk to the "external world"
Let's start with a few observation about async actions in general.
asyncActionVoid methods to create async action.
As you can see, an async action takes as a first parameter a dispatch delegate which allows the async action to dispatch synchronous ones. This comes useful when it is needed to update the app state. The second parameter is another delegate which allows us to get the app state - getState, useful if we need that info to make some decision about the behaviour. The third parameter is optional and is the only way for the one invoking the async action to pass some information to it(remember that currying part? this's why). If the action does not need any parameters at the time of the invocation, go ahead and create async action with only two parameters.
The result of an async action can be either
Task<T> and such async actions are created respectively by
asyncAction methods of the
Coming back to this specific action - it returns
Task and expects a parameter of type
LoginInfo at the time of its invocation, containing the username and password the user has specified. It dispatches actions corresponding to the progress and the outcome of the operation. Also worth noticing is it navigates to another view model upon successful authentication.
The app state
The login async action is dispatching actions before and after the long-running network operation, but how does that affect the view on the screen? - the actions are handled by the mini-reducer responsible for that part of the state and the state is updated accordingly.
Before we look at the reducer let's take a look at the declaration of the app state.
The idea here is that the complete state of the app is broken down into smaller chunks which are handled by dedicated mini-reducers. Divide and conquer my friends, divide and conquer .
The details about
DeviceListPageState are intentionally missing since it is not important for this discussion.
What follows is an abbreviated version of the
App class where all "app logic" lives and the reducer is an important part of it.
A few things here. The reducer for the store is a
CompositeReducer and it delegates the responsibility for different parts of the app state to other reducers, in this case - a couple of
SimpleReducers, but this sort of brake down can be nested further - here is an example.
Worth noting also is the constructor of
SimpleReducer where we can create the initial value for the state this reducer governs. If not provided - the state gets initilized with default values.
Let's take a look at the view model for the LoginPageView
I have a very simple base class for my view models -
ViewModel, which helps me find its view based on a very simple convention. Comes handy when I have to do navigation. I am also using the great Fody.PropertyChanged which saves me the annoyance of dealing with
As you can see the view model itself is quite simple. Dispatches the Login async action with the username and password the user has provided and listens for updates to the store and updates its properties accordingly.
Store.createAsyncActionCommand is an extension method that creates an
ICommand that dispatches an action and is something that is not part of Reducto, but might put in an Reducto.XamarinForms nuget package. For now, you can go see the source code
Needless to say this structuring of an app makes a lot more sense to me:
- The real logic of the app is in one place and not mixed with other concerns. To quote the late Yogi Berra
You can observe a lot just by watching
- Testing the logic is quite nice too. Reducers are very easy to test since they are so simple and all you need to do is give them state and an action and compare the result to what you expected it to be. Async actions which are the other half of the logic in the app need a bit more setup - mocking and stubbing but all in all pretty good experience too.
I know these are still early days for Reducto and may be there are some issues to be addressed but if you like where this is going, please open an issue and let's make it better.
The source code for the full sample app this post is based on can be found on GitHub.
Common guys, hit the comments and let me know what you think .
Alright, this is the thing that I have had burning desire to share with you. I think it is awesome. But before I get to that I would like to give you a quick outline how I got here.
And then I bumped into Redux and I loved everything about it - simple, powerful, versatile. It's like I head a blurry image of what I wanted and Redux just put everything in focus. I immediately knew it was exactly what I was looking for.
So I rushed out and made a port to .NET which I call Reducto.
Let me try to explain the basic concepts in Reducto:
- Action - an object which describes what has happened - LoggingIn, SignedOut, etc. The object contains all the information relevant to the action - username, password, status, etc. Usually there are many actions in an app
- Reducer - a side-effect free function that receives the current state of your app and an action. If the reducer does not know how to handle the action it should return the state as is. If the reducer can handle the action it makes a copy of the state, it modifies it in response to the action and returns the copy.
- Store - it is an object that contains your whole app's state. It also has a reducer. We dispatch an action to the store which hands it to the reducer together with the current app state and then uses the return value of the reducer as the new state of the app. There is only one store in your app. It's created when your app starts and gets destroyed when your app quits. Your MVVM view models can subscribe to be notified when the state changes so they can update themselves accordingly.
Having a single reducer operate on the whole state of the app probably sounds a bit scary but the trick is to compose the reducer from many smaller, simpler reducers which distribute the responsibility of updating different parts of the state. More on that in my next post.
The basic idea is to create a store and give it a reducer(a composite one quite likely). We dispatch actions to the store which, with the help of reducers, updates the state. Rinse and repeat.
Besides these core concepts in Reducto(and Redux) there are a couple more that are quite useful:
- Async action - a function that may have side effects. This is where you talk to your database, call a web service, navigate to a view model, etc. Async actions can also dispatch actions (as described in the core concepts).
- Middleware - these are functions that can be hooked in the Store dispatch mechanism so you can do things like logging, profiling, authorization, etc. It's sort of a plugin mechanism which can be quite useful.
Let's jump right in and see an example. Let's look at a very simple model of the app state which is only concerned with logging a user in. To make things simple I have presented this in the form of a unit test with the assertions showing what the expectations are
The example starts off with defining the app state - LoginState. Point of interest here is that it is a
struct. Remember that app state is passed to the reducer which is supposed to return a copy of it.
Structs have the benefit of being passed by value by default so they get copied naturally whenever they are passed as an argument.
Defined are two actions - LoginStarted and LoginSucceeded. They contain interesting information submitted from the user in the case of the former action and the authenticating service for the latter one.
On Line 31 we define a reducer for the app state and then we define how we handle both actions. On a side note, if an action is dispatched that is not handled by the reducer, he sends the app state unmodified back.
Line 45 defines the app store and let the store have its reducer.
Line 47 we see a subscription to the store, which gets notified whenever there is an update to the state. In line 70 we remove the subscription.
So here are the benefits that I see
- the logic is isolated in those reducers
- defining the reducers is very simple and does not require creating classes, inheriting or anything like that. A function is enough.
- testing is extremely simple - dispatch some actions, assert on the state
- framework agnostic - MVVM, MVC, WebForms apps can use it, but also this seems like an all around good way to structure all kinds of apps really.
A few interesting statistics regarding Reducto
|lines of code||~260|
In my next post I will dive a bit deeper in Reducto - Async actions and Composite reducers and connecting all of this in a Xamarin Forms app. The code for that post is already in development over here.
And please, do share your thoughts in the comments ;)
My last post Common problems with MVVM was it seems pretty vague and needs a concrete example to make a bit more sense. Not to mention in an attempt to not sound too authoritative I managed to represent myself like someone who is just starting with MVVM and and is taking too big of a bite.
Let's dive right into a typical example of a login page view model from a Xamarin Forms app:
In case you are wondering - I am using Fody.PropertyChanged to skip the minutiae of property change notifications.
The problem in my opinion is in the Login command - lines 23-25. Upon triggering the Login command the view model invokes the authentication process and in the case of success, navigates to the "main" view.
- The view model should not know about the authentication process at all. Neither when nor how it should be accomplished. It's not important to the view and makes it harder to test it. Should we decide to handle failure to authenticate, the code in that login command will get more complicated real quick.
- The view model should not know anything about the navigation process. Again neither when nor how. Not relevant to the view. It makes it more difficult to experiment with the view or use it in a different way - for example for Sign Up.
I think we would be better off with a view model more like this one:
Yes, I made up a pubsub "thing" that allows for publishing and subscribing ;-).
Generally speaking if view models are not the place for app logic, what kind of logic should be in there? I think we should have there animation logic, syncing states between controls in the view and in general logic directly connected to the view itself and its components.
Now this model is much simpler and rather easy to test. In fact it is so simple I think I just lost interest in testing it.
I would rather test the logic that controls it, and that is what I really wanted to get you curious about ;)
I will keep working on that new sample but meanwhile I encourage you to post your thoughts in the comments below.
Until next time.
Those few who have read my blog before might remember me raving about the combo Fody.PropertyChanged + MVVMCommand. I was pretty excited to get some good comments on that post and it is really the comments that are the reason I am writing this post.
So after being around AngularJS and React here are the things that I find wrong with most of the examples of MVVM apps I find on the web:
App logic is distributed and disconnected
This is the main point really and the other two are a consequence of it - there is no central app object or a system which controls the flow and holds the state of the app.
Too much logic and responsibilities in the
Since app logic has no special home it is spread over
viewsview models. Those are fetching data from services and transforming it to their own structure, doing navigation to other views, etc. These extra responsibility only make changing the flow of the app and reusing the views more difficult. I am talking about view models like this, this and this one. In my opinion viewsview models should receive data, take care of the user interactions in the view and send out notifications/actions with the data the user has submitted. Here is a link to a blog post by Adam Kemp that goes in detail about decoupled views.
Not good enough testing of the behaviour of the app due to issues described in the previous two points
Testing of the app is limited to testing individual
viewsview models, it is quite hard to test the flow of the app unless you are up for setting up a lot of infrastructure and usually plenty of mocking.
MVVM frameworks that are on the market either (inadvertently) support these design shortcomings (dependency injection containers make it super easy to get all kinds of services in the view model with minimal effort) or at best do not address them at all. To be fair these frameworks are mostly trying to smooth out the MVVM pattern and are not trying too hard to advise you on app architecture but their samples are doing that in a way.
So back to those comments mentioned earlier.
I wrote I am going to write my idea of a good mobile app and I would like to show you what I have been up to. Coming up real soon ;)
UPDATE: fixed broken links to the examples
Maybe it's a bit too early to start meta-posting but I am starting to doubt the decisions I took regarding this so may be if I go through the process of it I might feel a bit better.
GitHub does not provide any comment system as such, so the blogging story is somewhat incomplete. Which once again makes for a good challenge. Setting up a database and using it for comments is out of the question for me - have to host it somewhere, setting up all the processes around the management of comments - posting comments, reviewing, e-mail notifications, responding by e-mail, etc. It basically would erase the simplicity GitHub pages gives me.
At this point I get this idea that it would be really awesome if I can somehow get the GitHub issue discussion page and embed it into my blog. I checked out the GitHub issues API - looked good; I googled around and found this post by Ivan Zuzak essentially implementing the same idea. I stole a whole lot of code from that post but I need a few more things to make it really work well.
So what are the pros and cons with the GitHub-issue-as-a-backend:
- Open API to access the comments, accessible by anyone, which is right - I do not own anyone's comments
- GitHub handles sending email notifications as well as answering to comments by email
- Full control over the HTML/CSS of the comments section
- Trustworthy (so far) keeper of the data - GitHub
- commenters do need a GitHub account
I believe my current and potential readers are the kind of people who do have a GitHub account so hopefully this one drawback does not matter, but if you would like to comment and don't have an account please tweet at me and I would proxy on your behalf.
I think I feel better now ;-)
TL;DR - Super-clean MVVM example with automatic Command status updates here
Xamarin Forms is great for writing decent looking applications on all three major mobile platforms - iOS, Android and Windows Mobile. The Xaml support and databindings are great help to keep things clean and simple.
So I am working on a Xamarin Forms project where we were in the process of choosing some framework or library to help us with implementing the MVVM pattern and more specifically the view models where it is necessary to send notifications as properties get changed. We looked at MvvmLight as well as a preview version of Prism. Both of them provide a base class which has helper methods for implementing properties with sending notifications. Here is how a simple Login view model looks with Prism
A couple of things to notice here:
- Lot's of repetitive code in the property getters and setters
- Extra complexity in both the property setters and the command itself due to keeping the enabled/disabled state of the Login command in sync (lines 22, 30, 63 and 72)
- We need extra (and may I say not particularly pretty) testing of whether the property change notifications were sent and the update of the Login command status was triggered
In order to clean things up let's use Fody.PropertyChanged and get rid of Prism's BindableBase. If you have not looked at this MVVM specialized weaver, please take your time now to get introduced. I think you might like it. And here is how the new model looks like:
I think Fody.PropertyChanged is great, it helps us get rid of those super-boring, error-prone property getters and setters but unfortunately does nothing about updating the Login command state. In fact this version of the model does not work correctly since the Login command never gets enabled. One way to fix this problem is by making the Login command state be more MVVM-y. To do that let's use MVVMCommand which is exactly like a normal Xamarin Forms Command except the second parameter has to be a lambda that returns the value of a property. Let's see how the fixed model looks:
Notice how in line 28 the state of the command becomes extracted to a property - IsLoginEnabled. The reason that is good is because it plays on the strength of Fody.PropertyChanged. Meaning that Fody.PropertyChanged sends out ProprtyChanged notifications about IsLoginEnabled whenever one of the properties it's value is based on changes. Now all MVVMCommand needs to do is listen for those notifications and do the updating of the enabled/disabled command state. It's actually pretty simple:
Testing this model now becomes really a rather simple and elegant exercise of changing properties and asserting on the value of other properties and no need to worry about bookkeeping regarding who update who.
Hope this makes sense, the source for a complete example is on GitHub.
BTW: Sorry for not providing a way to do comments, now actively working on it, I promise. Meanwhile please create an issue on the example and let's have the conversation over there ;)
Update: As promised I have a way to post comments, but it's somewhat I-am-too-smart-for-my-own-good so I guess I will have to write a post about that ;)
In my last post I was going over the elements of a blog that I use in my little experiment. Now, for the rest ...
What I have not added is links to similar posts, my twitter stream, blog rolls, archive, like button, retweet button, G+ button and all kinds of other buttons that are mostly create noise, do not look esthetically pleasing and seldomly used. Other omissions include footer, site map, copyright notices and all kinds of what I consider cruft.
Finally for the invisible - I have what seems to be an Atom feed for the web site and that one is machine discoverable but I decided to not make an official link to it on the site. Basically if you go to a site like Feedly and paste the url for the blog, the feed link will be descovered and used. And that I think is enough visibility for that element. I think I will also try to surface that feed in the Google result if that is possible.
I am trying to get my first visual iteration of the blog and it is taking longer then I expect (which in itself was expected, sigh ...) so I thought while I am working on that I should first briefly note the visual elements, the concepts if you will, that I have included here and the ones I have not. My understanding of design is a big part of it is about making choices of what you offer and also what you do not offer, both parts on par in their importance.
For the accidental visitors of my blog I would like them to first and foremost notice the content and then may be if they are interested make it easy get a feeling for who the auther is (that would be me).
Content is a list of posts, each of which has a title, content and date of creation. That has to be the minimum with the obvious omission of comments. I mean I could have skipped date but I do find myself looking at the date of other people's posts so why shouldn't I do the same, right?
Comments are not here mostly due to not having a particularly clear idea how to add them to my statically hosted blog. I have a feeling they are coming soon though.
Author is the section where the visitor can get some feeling about the person writing those words. A picture and a name should be a good start. The name itself is a link to the very first post in this blog where I indulged myself in a bit more of an introduction of my character. Thought that level of intimacy should require the trouble of clicking a link ;)
GitHub and Twitter links to my accounts would reveal a bit more about me should the visitor feel compelled to find out more about yours truely.
I am pretty happy about my selection of elements, still it is definitely subject to change.
Next time - the invisible and all the things I did not add ...
So I am all fired up about not sucking so much designing web and mobile apps and I though I really need some exercise. I recently read this great book - Design for Hackers: Reverse Engineering Beauty and I thought - why not start a blog in the rawest form possible and little by little try to make it better. Looking forward to iteration one ;)
Hello, my name is Petar Shomov and this is my blog. I am a software developer with about 20 years in the industry. I think I am doing and learning about interesting things so I thought I should share them and probably do them even better because of that. I am interested most recently in design of all things digital but I like architect-y things as well as programm-y things. I work mostly in OSX and Linux but I have spent way too much time in Windows too.