Originally from https://www.leaware.com/post/creating-cross-platform-xamarin-applications-using-the-mvvmcross-framework
This article outlines the basic principles and mechanisms of the MvvmCross framework, which facilitates the creation of loosely coupled, maintainable, and testable mobile solutions. There are many tested methods of organizing projects to create an individual architecture for each project based on a Shared Project or Portable Class Library.
However, it is more effective to use a ready-made cross-platform frame solution, which will define the structure and basic mechanisms of its operation; will provide a set of libraries, components, and plugins; which speeds up the development process. Among many popular frameworks, such as Xamarin.Forms, ReactiveUI, FreshMvvm, or Prism, MvvmCross deserves special attention. It offers a good compromise between a high-quality UX (User Experience) and the amount of code shared across projects.
WHAT IS MVVMCROSS?
As the name suggests, MvvmCross is a framework facilitating the creation of cross-platform applications conforming with the MVVM model (Model-View-ViewModel). It supports many popular types of .NET projects, such as:
Xamarin.Android
Xamarin.iOS
Xamarin.Mac
WinRT (Windows 8.1, Windows Phone 8.1)
Universal Windows Platform (UWP) (Windows 10)
Windows Presentation Foundation (WPF)
It also provides mechanisms for data binding for platforms that natively use the MVC model (Model-View-Controller).
MvvmCross applications usually are composed of two fundamental parts:A core project built around a Portable Class Library (PCL), containing all the viewmodels, models and interfaces of platform-specific services. The core PCL carries the business logic, database handling and a layer of access to web services. A native project for each platform, containing the user interface and implementation of platform-specific services. It is a good practice to create an additional PCL project in order to separate the application business logic from the data access layer.
The amount of shared code changes depending on the application type. Of course, if our application uses more of the native API, a smaller part of the solution will be used again. In the case of business applications it is possible to share about 70-80% of the entire solution.
In order to start your adventure with the MvvmCross framework, you need to create a solution containing all the necessary projects: at least one PCL library and a native project for each platform you plan to support. Next, you need to add a NuGet MvvmCross Starter Pack to each of them and make a few basic configuration steps described in the files contained in the ToDo-MvvmCross directory.
The entire MvvmCross framework, together with documentation and video tutorials, is available on GitHub.
BASIC ELEMENTS OF THE FRAMEWORK
Every MvvmCross application has certain elements: an App class, a Setup class and viewmodels. This section examines those parts along with typical implementations.
MvvmCross provides the static Mvx class, which functions as a container for injecting dependencies and is responsible for managing implementations registered both in the common part and in the platform-specific projects in the Setup class.
The Setup class is a kind of bootstrapper for MvvmCross, and is present in every platform-specific project. Project Xamarin.Android is an example. The basic task of this class is to create an instance of the App class, as well as to adjust the framework to the specifics of our application.
The MvxAndroidSetup class, from which the Setup class inherits, provides a series of virtual methods that need to be overridden in order to, among other things, register all the platform services (referring to the native API). The platform-specific services are used in the common part in order to execute instructions specific for every platform – the Inversion of Control mechanism.
Another important element of the MvvmCross solution is the viewmodel, which functions as a container for properties and commands responsible for changing the state and retaining the view related to it.
The base class of the presented fragment of a viewmodel (Listing 3) contains an implementation of interfaces INotifyPropertyChanged, INotifyCollectionChanged, and the methods such as SetProperty or RaisePropertyChanged. These interfaces and methods allow the system to refresh elements of a view: when certain properties change they trigger an event that conveys that change through the UI.
Commands implemented using the MvxCommand class manage individual actions performed by the user, e.g. changing a view. MvxViewModel provides many useful methods for functions such as navigation between viewmodels (ShowViewModel) or management of view lifecycle. The job of the Mvx container is to automatically inject dependencies into created viewmodels.
DEFINING THE USER INTERFACE: HOW TO CREATE VIEWS
The data binding mechanism is a natural element of the Windows ecosystem (WPF, WinRT, and UWP), so in Windows, the method for creating views uses the native approach. Therefore, let’s concentrate on the Android and iOS platforms, for which the native model is MVC, where controllers (iOS) and activities/fragments (Android) play a key role.
An exceptional advantage of the MvvmCross framework is the fact that contrary to Xamarin.Forms all the views, layouts, widgets are defined entirely natively, using native mechanisms and tools.
Xamarin.Android
In the case of Android (Listing 4) we create xml or axml files (or axml) that use only the native API for constructing layouts for individual views. MvvmCross provides the local: MvxBind attribute, which can be used for binding properties of the elements of the view (widgets, layouts) with appropriate properties of a viewmodel according to the above listing.
A framework provides an additional set of UI controls. One of them is a widget (MvxListView) that displays a list of elements. MvxListView specifies a template of a cell of a given list using the local:MvxItemTemplate property. Implementation of an adapter is unnecessary, so we avoid excess code in the platform-specific project.
In MvvmCross, controllers are used only to load the view and bind it with the correct viewmodel (Listing 5). Of course, if the application requires providing certain specific functionalities/actions on a given platform, they can be implemented in the controllers. However, one must remember that this will reduce the amount of code that can be shared, and possible differences or inconsistencies in application behavior make debugging and implementing additional changes more time-consuming.
Xamarin.iOS
The creation of a view for the iOS system starts with adding a controller class together with a file with a xib extension which represents the view. Edit xib files with Xamarin Studio (the environment provided by Xamarin) or the Xcode Interface Builder tool built-in in Xcode.
After arranging the view, hold the CTRL-key and drag the elements that you want to bind with the viewmodel to the appropriate header file. The changes will be automatically synchronized to the C# language, and more specifically to the class of the created controller (Listing 6). Properties of the controller marked with the attribute Outlet are called outlets. Through them, we get access to individual elements of the user interface.
After loading the view we create a set that binds properties of available outlets with the properties of a given viewmodel. The Bind method accepts the object (usually an outlet), which we will consider. The For method specifies the object property which will be bound with the property of the viewmodel specified by the To method. If the For method is skipped, the default property of a given outlet is bound.
ADVANCED DATA BINDING – CREATING CONVERTERS AND CUSTOM BINDINGS
Frequently, composed views require additional conversion (translation) of bound data, i.e. changing the type or format of viewmodel properties, which gets bound with the property of a given UI control. For this purpose, it is possible to define a converter implementing an abstract class MvxValueConverter.
How to apply the defined converter? In the case of Xamarin.iOS it comes down to requesting the WithConversion method, which assumes the converter instance.
Xamarin.Android seems to be even more intuitive – it is enough to connect the bound property with the name of our converter.
Frequently, it could occur that a specific property of a viewmodel determines the change of a few properties of a widget or requires changing the widget, which can be performed only by requesting calling one or a series of methods for it. In such a case the mechanism allowing for registering custom data bindings becomes necessary.
The first step is to create a class inheriting from the MvxConvertingTargetBinding class. In the presented example we defined a binding for the text value containing HTML markups. In order to interpret them correctly, the SetText method needs to be used with appropriate parameters and the property MovementMethod of the TextView widget must be changed – we are not able to do it effectively, based on default bindings.
Next, overload overrides the FillTargetFactories method in the Setup class, registering the created binding under a selected name with an appropriate type of UI control. The entire process is analogous to the Xamarin.iOS system. A registered binding can be successfully used in the entire application in the same way as standard predefined data bindings.
CHANGING THE STANDARD NAVIGATION SCHEME
Each platform-specific MvvmCross package contains a default presenter implementing the IMvxViewPresenter interface. The presenter is responsible for providing a navigation scheme between certain views. By default it uses the mechanism of reflection for associating controllers with viewmodels corresponding to them, therefore the key element is to give names to controllers that correspond to the names of viewmodels used in the PCL project.
If for some reason we want to change the default navigation scheme, it is enough to override appropriate methods of the default presenter, and after that return it in the CreateViewPresenter method in the Setup class (Listing 14). We deal with this situation, for example, when there is a need to associate a certain group of viewmodels with fragments displayed in the area of main activity – flyout navigation.
UNIT TESTS
Undoubtedly, unit tests are some of the most important elements in the process of ensuring the high quality of the created software. Using the MvvmCross framework requires programmers to create a testable architecture of the entire solution, thanks to which, without too much workload, we are able to write unit tests for particular elements of our business logic. The NUnit framework recommended by Xamarin executes this function perfectly.
Writing tests must start with adding to our testing project the following set of NuGet packages: MvvmCross, MvvmCross.Core and MvvmCross.Tests. Naturally, one also needs to ensure that references are attached added to the project with the business logic of the application, as well as tools allowing for quick imitation of objects – one of the more popular is the Moq framework.
The MvvmCross.Tests package contains the MvxIoCSupportingTest class, which is a base class for every newly created testing class (Listing 15). Using the Ioc property we register all the types necessary for creating a viewmodel which we are going to test (in the example I mock IProductRepository using the Moq framework).
The Mvx container is responsible for creating a viewmodel and providing all the necessary dependencies. Next, we assign test values to specific properties of the viewmodel and we test the behavior of the remaining ones. In practice, they will determine the changes in the view state associated with the tested viewmodel. The SetUp class and test instances are specified by using standard attributes of the NUnit framework.
PLUGINS AND ADDITIONAL COMPONENTS
The MvvmCross framework provides a large number of plugins and libraries available on GitHub, as well as in the form of NuGet packages. They are, among others, components for simplifying database operation, network availability, connections, locations, operations on files, sending e-mails, integration with social networks, downloading, and storing data in cache memory. All we need to do is add an appropriate package to all the projects in a solution and then use an available API in the common part. There is also a possibility to create your own internal plugins or developing existing ones in accordance with the instruction available.
SUMMARY
This article presents only selected, most significant mechanisms of the MvvmCross framework. The discussed solution is continuously developed, providing more and more new possibilities. It is undoubtedly the best solution for complex and demanding Xamarin business applications.
Thanks to native methods of building a user interface we can deliver a great UX, and at the same time share a significant part of the entire solution including testable business logic of the application.
I had the pleasure of participating in complex projects realized with the use of this technology composed of dozens of screens and functionalities, such as mobile banking applications for serious foreign clients, which from the moment of publication have received the highest ratings from hundreds of users, which is the best proof that the presented solution is effective.
Top comments (0)