I’ve been doing a lot of work with the MvvmCross framework lately, and so far have been really enjoying it. As I was building out my application, I found myself wanting some functionality along the lines of Android’s StartActivityForResult method, where the application would navigate to some screen that would return a result back to the calling screen. In iOS this kind of thing would normally be done with a modal view controller. Since this is not built into MvvmCross, I tried to come up with a pattern for allowing this sort of behavior. In this post I’ll outline what I came up with.
In order to allow for communication between different pieces of the system, I am using an event aggregator to help keep everything decoupled by using the publish/subscribe pattern. In this example I am using TinyMessenger, but you can substitute any similar service if there’s one you like more. To easily give all view models access to the aggregator, first we’ll create a common base class for them to inherit from:
This assumes that you’ve already registered a singleton in your IoC container for ITinyMessenger. Now that the aggregator is exposed, we can define the message to be used to communicate that a a result is being returned from a child view model:
There’s not much going on in the message here. It’s simply a generic transport for the result of of a child view action in our application. Next, we can define a subclass for view models that are to be used for fetching results:
The constructor takes in a message ID parameter, which is expected to be a unique value in the system that identifies this particular request. You will see this in use in the next section. The real functionality here comes in the ReturnResult() and Cancel() methods. When implementing the actual view models, these would be exposed in commands that can be triggered from the view. I didn’t want to impose any limitations on how they are exposed to start off with, so I left them as simple method calls. When returning a result it creates and publishes a new instance of the message defined in the last section. After that it closes the view model, which will return flow to the previous screen.
Now we have the message defined and the sub-view that creates it, so all that’s left is giving view models a way to create the request. In order to do this, we’ll add a new method to the ViewModelBase class defined earlier called RequestSubNavigate(). I debated a bit on the name, but decided to stay consistent with the MvvmCross method naming here.
This method creates a message ID by simply creating a new GUID, but you could also substitute in your own method here if you had some other mechanism for maintaining unique identifiers. The reason for message IDs is wanting to be able to distinguish between different messages in the event you have nested child views and multiple parents are listening for results. When the result is received, the view model unsubscribes from the message to help clear the reference, andn invokes the callback method provided.
That’s all you need to facilitate communication between view models. In order to test things out, let’s set up some tests. First, let’s define a base class for view model tests that creates the IoC container, some mocks, and exposes the event aggregator. This example uses the mocking classes defined in Stuart’s example on his blog.
With that defined, we can write some tests to make sure things are working properly:
If all went smoothly, your tests should be green! I’m using NUnit here, but you can substitute in your favorite unit testing framework. Specifically, I’m using the NUnitLite subset which allows the unit tests to be used in MonoTouch and Mono for Android unit test projects as well.
This Isn’t Perfect
There is one obvious problem with this pattern that I want to mention. If the user switched away from your application and your app got cycled out of memory, the event handlers wouldn’t be in the right state when the application got resumed later on. For now I decided to leave this unsolved since I’m still working out how my application should behave in this scenario, and also didn’t want to impose limitations right from the start. One option could be to persist message subscriptions, such as to a database or file, and then reload them when the application restarts itself. As always, this is a tricky rabbit hole that many apps already don’t handle well, so it remains a problem right now with this pattern as well.
Hopefully you find this pattern useful in your applications as well. If you have any other patterns you’re using to handle this kind of scenario, also please let me know!