I recently decided to introduce a UITabBarController into our MvvmCross iOS application. The navigation stack was getting a bit deap and the tabs provide a quick way back to the root. Stuart Lodge has a excellent video series on MvvmCross and just so happens to have one on using Tabs.
In the video he shows how to hide the navigation bar of the root UINavigationController so that you don't land up with a double bar on top. This is all fine for demo purposes (Stuart has thick skin and is only interested in progress) but what we really want is is for the UITabBarController to be the root controller. Apple recommends this approach and I have experienced some weirdness when not following this rule (especially when applying themes).
Unfortunately the presenter below does require a small change to the MvvmCross libraries.
HomeView is your MvxTabBarController which we will keep a reference to. Show is overridden to save the reference and sets HomeView as the root view controller using the base method. If anything else but the HomeView is shown the base Show is called. MasterNavigationController will return the UINavigationController of the currently selected tab. The idea is that you have a UINavigationController as the root controller in each tab. CurrentTopViewController simply returns the top most view of MasterNavigationController.
The change in the MvvmCross library I mentioned earlier is in MvxTouchViewPresenter. In order for the above to work the following change is required:
What we really need is a base presenter that doesn't force a UINavigationController as the root. Of course you can roll your own Presenter from scratch (MvvmCross is flexible that way) but that's a lot more code. Something to work on for next time...
Thursday, June 6, 2013
Tuesday, April 23, 2013
Using Mvx Messenger for loosely coupled communication
A ViewModel often needs to know of some status change in a service class. For instance you may have a network connection that you keep open to your platform. The ViewModel may want to know when this connection goes up and down.
.NET Events
When your ViewModel is created it subscribes to the event you interested in monitoring.
This works just fine. When the connection state changes you get notified and raise the fact that the IsConnecting property on your ViewModel has changed. Any UI that is bound to this property can update accordingly.
The problem with this approach is that the INetworkConnection outlives the ViewModel. Whats supposed to happen is when the View leaves the screen it gets garbage collected along with the ViewModel it owns. Now the INetworkConnection has a reference to the ViewModel (created by the event subscription) effectively preventing the ViewModel from getting GC'd. Of course we can make sure we unsubscribe from the event at some point but this has its caveats and becomes messy. We need a better way...
This works just fine. When the connection state changes you get notified and raise the fact that the IsConnecting property on your ViewModel has changed. Any UI that is bound to this property can update accordingly.
The problem with this approach is that the INetworkConnection outlives the ViewModel. Whats supposed to happen is when the View leaves the screen it gets garbage collected along with the ViewModel it owns. Now the INetworkConnection has a reference to the ViewModel (created by the event subscription) effectively preventing the ViewModel from getting GC'd. Of course we can make sure we unsubscribe from the event at some point but this has its caveats and becomes messy. We need a better way...
Enter the MvxMessenger
OK so these Message Aggregators have been around for a while. I've used TinyMessenger in the past when using V2 of MvvmCross. V3 introduces an MvxMessenger in the form of a Plugin. To get started make sure you register the plugin in your Setup class.
In this example my ViewModel registers interest in a message inside its constructor.
MessageHub is a property on my ViewModel base class that just returns an instance of the IMvxMessenger plugin we registered in the beginning. Something worth noting is that the token returned is stored in a field within the ViewModel. MvxMessenger uses a WeakReference to the Action<TMessage> when you subscribe. If you don't hold on to the token the reference gets lost and you wont receive your message. Something else to consider is the thread where your Action delegate (the one you passed to IMvxMessenger.Subscribe) gets called. In this example it will be called on whatever thread the INetworkConnection calls the IMvxMessenger.Publish() method on. Checkout IMvxMessenger.SubscribeOnMainThread which is especially important if your Action code calls into the UI. Using RaisePropertyChanged in your Action code is a prime example.
The Publish() method (already mentioned above) is used to send a message to subscribers. In this example its called by the INetworkConnection when its state changes. CoreStatusMessage is a MvxMessage that you can customise as you please.
In this example my ViewModel registers interest in a message inside its constructor.
MessageHub is a property on my ViewModel base class that just returns an instance of the IMvxMessenger plugin we registered in the beginning. Something worth noting is that the token returned is stored in a field within the ViewModel. MvxMessenger uses a WeakReference to the Action<TMessage> when you subscribe. If you don't hold on to the token the reference gets lost and you wont receive your message. Something else to consider is the thread where your Action delegate (the one you passed to IMvxMessenger.Subscribe) gets called. In this example it will be called on whatever thread the INetworkConnection calls the IMvxMessenger.Publish() method on. Checkout IMvxMessenger.SubscribeOnMainThread which is especially important if your Action code calls into the UI. Using RaisePropertyChanged in your Action code is a prime example.
The Publish() method (already mentioned above) is used to send a message to subscribers. In this example its called by the INetworkConnection when its state changes. CoreStatusMessage is a MvxMessage that you can customise as you please.
Conclusion
The great thing about this is you don't have to be concerned with holding on to objects and causing memory leaks. Consider using this instead of regular events in your MvvmCross app's. Especially where short lived Views need event's from some of your long living service classes.
Wednesday, March 20, 2013
Updating my mobile apps for Async
Xamarin recently announced support for Async in the form of an Alpha release. Time to update some code to take advantage.
Above is the change made in my service class that log's a user in. The LoginSession class was already returning a Task. I removed the ugly ContinueWith and the 2 delegates passed in. Now I just return the Task.
In my LoginViewModel I can make the changes above. Much cleaner. Note how the lambda passed to the RelayCommand is marked as Async. This is the magic that allows me to wait on the CsService.Login call. Once the Login completes I can continue where I left off in the UI thread and close the login dialog or handle any exceptions. Both the LoginComplete and LoginFailed methods are completely removed and now handled in the same order you would expect if there was no need to wait for the login to go out and make a network call (which takes time).
If you just getting into async I can recommend watching the video series by Lucian Wischik Three Essential Tips for Async
Above is the change made in my service class that log's a user in. The LoginSession class was already returning a Task. I removed the ugly ContinueWith and the 2 delegates passed in. Now I just return the Task.
In my LoginViewModel I can make the changes above. Much cleaner. Note how the lambda passed to the RelayCommand is marked as Async. This is the magic that allows me to wait on the CsService.Login call. Once the Login completes I can continue where I left off in the UI thread and close the login dialog or handle any exceptions. Both the LoginComplete and LoginFailed methods are completely removed and now handled in the same order you would expect if there was no need to wait for the login to go out and make a network call (which takes time).
If you just getting into async I can recommend watching the video series by Lucian Wischik Three Essential Tips for Async
Wednesday, February 6, 2013
IOC Container
My objective here was to create a lightweight container using some of the newer .NET classes available. Requirements included thread safety and lazy loading.
This is more an implementation of the service locator pattern and definitely not a fully kitted IOC container as the name suggests. For someone who requires simple service registration and location from multiple threads with the added benefit of lazy loading this may be of some use.
Example Usage
Lets say you have an application consisting of a core project that contains code that you intend to reuse (for instance a portable class library) and a platform specific project that contains your UI. The UI project would register the Rope as the IPullable implementation (typically at startup). Now your core code can locate an implementation of IPullable whenever it needs it. When you port your project to another platform you can register a different implementation of IPullable (ElasticBand for example). Your core code remains unchanged and is non the wiser that its now pulling an elastic band instead to a rope.
Tuesday, February 5, 2013
Cross Platform App Video (Droid)
I've been hard at work porting our iOS application over to other platforms. I recently posted a video showing the application running on the Mac, well I've done the same for Android.
MvvmCross and Xamarin are proving to be a very powerful combination. I believe I'm sharing about 80% of the code across these 3 platforms. More importantly the code that is shared is the tricky stuff (networking IO, protocols, database etc). I'll be digging deeper into the architecture in future posts. Stay tuned.
DeapExtensions Abbreviated NameSpace
Stuart Lodge just pointed out that I could use an abbreviated namespace so that the full DeapExtenions name does not have to be typed out in your Views. @cheesebaron has a blog post thats explains how to use it http://blog.ostebaronen.dk/2012/12/adding-view-namespace-abbreviations-in.html
I've updated the DeapExtensions to take advantage of this shortcut. Now you can just declare the BindableGroupListView as DeapExt.BindableGroupListView
There, much neater :) Don't forget to the use the DeapExtensions.Binding.Droid.BaseAndroidBindingSetup as your base for your setup class. It simply registers the DeapExt with MvvmCross.
I've updated the DeapExtensions to take advantage of this shortcut. Now you can just declare the BindableGroupListView as DeapExt.BindableGroupListView
There, much neater :) Don't forget to the use the DeapExtensions.Binding.Droid.BaseAndroidBindingSetup as your base for your setup class. It simply registers the DeapExt with MvvmCross.
Sunday, February 3, 2013
MvvmCross.DeapExtensions
Stuart Lodge suggested I start a separate library of extensions for the excellent MvvmCross framework. Meet MvvmCross.DeapExtensions. The first addition is a grouped list view. Those familiar with the UITableView Section in UITableView on iOS will know exactly what I'm talking about. Its essentially a list with group headers separating items that belong together.
My model consists of profiles and each profile has a list of devices that belongs to it. Binding the BindableGroupListView to the list of profiles automatically gives us the result we after.
Here is my Activity with the the grouped list declared
The important part is the MvxBind which binds the ListView to the SearchedProfiles property in my ViewModel.
The Item and Group Template are regular Android Layouts containing TextViews. These TextViews are bound using MvvmCross directly to there respective model objects. So list item_profile was bound to the ProfileName property on Profile and listitem_device which has 2 TextViews was bound to Device and its corresponding propertys.
BindableGroupListView works by assuming the list of items you bind to it are groups and that these groups which are are enumerable contain the items you wish to see in the group.
Lastly, notice how I also declare the ItemClick to DeviceSelected in the bind command. DeviceSelected is an ICommand declared in the ViewModel. DeviceSelected gets the Device selected and navigates to another Activity.Right now clicks are ignored on the groups. I've added GroupClick to support click handling on the group items.
My model consists of profiles and each profile has a list of devices that belongs to it. Binding the BindableGroupListView to the list of profiles automatically gives us the result we after.
Here is my Activity with the the grouped list declared
The important part is the MvxBind which binds the ListView to the SearchedProfiles property in my ViewModel.
The Item and Group Template are regular Android Layouts containing TextViews. These TextViews are bound using MvvmCross directly to there respective model objects. So list item_profile was bound to the ProfileName property on Profile and listitem_device which has 2 TextViews was bound to Device and its corresponding propertys.
BindableGroupListView works by assuming the list of items you bind to it are groups and that these groups which are are enumerable contain the items you wish to see in the group.
Lastly, notice how I also declare the ItemClick to DeviceSelected in the bind command. DeviceSelected is an ICommand declared in the ViewModel. DeviceSelected gets the Device selected and navigates to another Activity.
Subscribe to:
Posts (Atom)



