Sunday, 5 July 2015

Revisiting Prism for Xamarin.Forms

Previously I wrote about how to create a flexible and maintainable Xamarin.Forms app with Prism. In the last month Prism for Xamarin.Forms has undergone some more work by Brian Lagunas, and a couple of new features have been implemented. This blog post will investigate the new features provided by the latest release of Prism for Xamarin.Forms.

Navigation

There are two improvements made to navigation in the latest release of Prism for Xamarin.Forms. The first concerns how you register types for navigation, and how to perform navigation to registered types. An overload to IUnityContainer.RegisterTypeForNavigation has been added that accepts two generic types. The first argument is the View type, with the second argument being a Class type (most likely a ViewModel type).

1 protected override void RegisterTypes()
2 {
3 Container.RegisterTypeForNavigation<PhotoPage, PhotoPageViewModel>();
4 }

A corresponding change has been made to INavigationService.Navigate method to accept a generic in place of a string to identify the View to navigate to.

1 public void Navigate()
2 {
3 ...
4 this.NavigationService.Navigate<PhotoPageViewModel>(parameters);
5 }


The advantage of this approach is that it allows you specify a ViewModel for navigation, eliminating the magic string that was previously used.

The second improvement relates to how the correct INavigationService instance is obtained in a ViewModel for navigation. The previous approach involved having your ViewModel implement the INavigationServiceAware interface. in order to add a NavigationService instance to your ViewModel. In the latest release of Prism for Xamarin.Forms the INavigationService can be directly injected into the ViewModel constructor.

1 public MainPageViewModel(INavigationService navigationService)
2 {
3 this.NavigationService = navigationService;
4 ...
5 }

Please note that in order for Prism to inject the proper INavigationService instance via the constructor, you must name the parameter navigationService. This is because each page in Xamarin.Forms has its own navigation instance. So Prism can’t just register a global navigation service for all pages. It has to have a navigation service specific to each page and ViewModel. Prism uses the Unity dependency injection container to resolve the service through constructor injection, and because Unity doesn’t allow you to look for a type Prism instead looks for a specific name, in this case navigationService.

DependenyService improvements

Xamarin.Forms provides the DependencyService class to enable shared code to easily resolve interfaces to platform-specific implementations. However, it is not always easy to test ViewModels that make a call to the DependencyService class. A typical implementation without Prism is shown below.

1 public MainPageViewModel()
2 {
3 _textToSpeech = DependencyService.Get<ITextToSpeech>();
4 SpeakCommand = new DelegateCommand(Speak);
5 }
6
7 private void Speak()
8 {
9 _textToSpeech.Speak(TextToSay);
10 }

Prism improves this experience by enabling you to ask for your dependency as an argument in your ViewModel constructor.

1 public MainPageViewModel(ITextToSpeech textToSpeech)
2 {
3 _textToSpeech = textToSpeech;
4 SpeakCommand = new DelegateCommand(Speak);
5 }
6
7 private void Speak()
8 {
9 _textToSpeech.Speak(TextToSay);
10 }

The advantage of this approach is that it eliminates the call to the DependencyService class, helping to make your ViewModel class more easily testable. For an example that demonstrates using Prism with the TextToSpeech see this sample app.

Summary

Prism for Xamarin.Forms provides loosely-coupled components that help you to produce Xamarin.Forms apps that are flexible, maintainable, and testable. The latest additions to Prism enhance the navigation experience and make it easier to test ViewModels that use the Xamarin.Forms DependencyService class.

The sample app can be downloaded here.

2 comments:

  1. Hi Dave.

    One quick question. In the above example, Prism automatically resolves the dependency by adding it to the constructor of view model. Am in a scenario, where am connecting to sqlite and is using dependencyservice for platform specific code. In my database class (as per documentation in xamarin docs) , am using DependencyService.Get to retrieve the correct one.

    What is the best way to do it as per Prism ?

    Thanks
    Anu

    ReplyDelete
  2. I think that's one best aimed at Brian Lagunas. I've not been keeping up with Prism. I'm not sure if it's capable of resolving a dependency in a different assembly.

    ReplyDelete