Share this post

  

Creating a shared UI app for Android & iOS with C# and MVVM

The Model-View-ViewModel (MVVM) Pattern was introduced earlier in 2005 as a way of developing WPF desktop applications (Windows Presentation Foundation). However, most of its key concepts remain usable today as other technologies are including more support for MVVM-like features including Bindings and Command capabilities as they evolve.

Nowadays, a lot of MV-W(whatever) related technologies got some level of MVVM support like KnockoutJS, Angular, KendoUI and other web and non-web technologies and start bringing Bindings as first-class citizens and allow to combine them with Command Pattern and Asynchronous method invocation (AMI) implementations.

A practical MVVM example:

In an overview, MVVM is highly recommended for:

  • Scale-out your views as your project grow and stay organized.
  • Separation of concerns between designing UI and focus on logic separately.
  • Allowing Testability in your application (mocking, Test Driven Development, Behavior Driven Development and beyond).
MVVM Overview

Let’s see how you can take advantage of MVVM concepts and re-use those practices to develop a cross-platform mobile application and take advantage of Commands, Bindings and more.

The View

The View

The view might contain two important elements:

  • Bindings (which is the glue to make the View work with our ViewModel)
  • Commands (abstracts the main actions of the views, including clicks, taps, gestures and more)
  • States (portions of the view capable of reacting depending on the state of the ViewModel)

Here is the code for the shared UI between iOS and Android

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
   <local:CardView HeightRequest="70">
<StackLayout Orientation="Horizontal">
<Image Aspect="Fill" HeightRequest="30" WidthRequest="70">
<Image.Source>
<UriImageSource Uri="{Binding FlagSource}"
CacheValidity="3"
CachingEnabled="true"/>
</Image.Source>
</Image>
<StackLayout Orientation="Vertical"
Margin="10,0,10,0"
VerticalOptions="FillAndExpand">
<Label Text="{Binding Model.Name}" LineBreakMode="TailTruncation"
Margin="0" FontSize="20"
TextColor="Black" />
<Label Text="{Binding Model.Region}"
Margin="0"
TextColor="#C0C0C0" />
</StackLayout>
<StackLayout.Margin>
<OnPlatform x:TypeArguments="Thickness">
<OnPlatform.iOS>15,10,0,10</OnPlatform.iOS>
<OnPlatform.Android>5,5,5,5</OnPlatform.Android>
</OnPlatform>
</StackLayout.Margin>
</StackLayout>
<local:CardView.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BrowseCommand}"
CommandParameter="{Binding}"
/>
</local:CardView.GestureRecognizers>
</local:CardView>

Layout is shared between iOS and Android and rendered native on each platform. Shared functionality includes abstracted controls, layout containers, Commands, Bindings with minimum platform specifics and native control embedding, which are also supported if prefered to use AXAML or XIBs implementations.

The ViewModel

The ViewModel responsibility is to keep properties and data to be served to the view without worrying about how it looks. As mentioned before, it can keep the state of the view and handle observable actions and elements

There are two important elements here that allow decoupling your ViewModel and handle the UI in sync:

  • ObservableCollection - A generic class that allows our view to be observed and keep in sync with our ViewModel
  • PropertyChanged notifications - Specific notifications that can be attached to the properties to keep Two-Way communication between the View and ViewModel)

Let’s see a ViewModel class for a Search operation and focus on the results.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class CountrySearchViewModel : BindableObject
{
public CountrySearchViewModel(IEnumerable<CountryViewModel> countries)
{
Results = new ObservableCollection<CountryViewModel>();
Countries = countries;

OnFilter();
}

public IEnumerable<CountryViewModel> Countries { get; }

private string _searchText;
public string SearchText
{
get { return _searchText; }
set
{
_searchText = value;
OnPropertyChanged(nameof(SearchText));
OnFilter();
}
}

public ObservableCollection<CountryViewModel> Results { get; private set; }

private void OnFilter()
{
Results.Clear();

var query = string.IsNullOrEmpty(SearchText) ?
Countries.ToArray()
: Countries.Where(item => item.Matches(SearchText)).ToArray();

foreach (var item in query)
{
Results.Add(item);
}
}
}

The ViewModel allows to keep UI in sync with observable collections and properties hooked into the controls. Furthermore, you can take some of the recently introduced C# 6 features.

ViewModel hooked

ViewModel interaction in each platform (side-by-side) built with Commands, Bindings, and Navigation.

The Model

Then the Model class can act like the DTO of your application and come as needed, mean a Database, Web Service, a file, etc with domain-specific logic when required.

1
2
3
4
5
6
7
8
9
10
public class Country
{
public string Name { get; set; }
public string Region { get; set; }
public string SubRegion { get; set; }
public int Population { get; set; }
public string NativeName { get; set; }
public string Alpha2Code { get; set; }
public string Demonym { get; set; }
}

Handling Platform abstractions & specifics

By having this separation of concerns, other important can be abstracted and adapted into the app to provide common functionalities for the application including:

  • Screen-to-Screen Navigation (which can be abstracted to have ViewModel-to-ViewModel navigation through the application).
  • Third-party communications (Authentication, Local Storage, WebServices, etc).
  • Device capabilities & features (Bluetooth, Camera, Accelerometer, etc).
  • Asynchronous & background processing.

Commands, Bindings and ViewModel communication:

1
2
3
4
<ToolbarItem Command="{Binding SearchCommand, Source={x:Reference RootPage}}"
Order="Secondary"
Text="Search" />

By declaring the Bindings they can be filled with the Commands declared in the ViewModel and arrange the navigation as the following:

1
2
3
4
5
6
7
8
9
10
    SearchCommand = new Command(async obj => await CallSearchAsync());

//... later in the code

private async Task CallSearchAsync()
{
var searchPage = new SearchPage(Countries);

await NavigationService.PushAsync(searchPage);
}

MVVM Highlights & considerations.

  • Understand MVVM is a key, and a responsibility to carry-on. You still need to consider if MVVM might be good for your application and take advantage of it when necessary and prevent you from overkilling your apps in case you are building a slightly simple application.
  • Reuse the concepts you may already know. Taking ahead of MVVM and can allow you focus on the architecture of your app as you to understand the basics of the underlying technology (desktop, the web, mobile) and platform specifics.
  • Adopting an MVVM Framework? Keep in mind: Frameworks are optional. Use them as needed since you might start with a pure MVVM implementation and eventually bring an MVVM toolkit but only for solving a MVVM problem in your app (lots of Views, ViewModels, ViewModel communication, navigation, etc) and flavors vary between different implementations including MvvmLight, MvvmCross, Prism as some of them are adding Reactive programming features like ReactiveUI.
  • Consider starting MVVM “pure” and your UI/project becomes wide and complex. An MVVM solution/framework/library and avoid to overwhelm your app with stuff that you might not need.
  • Bindings are spreading out. Take them seriously. Before coding your own screens and UI interactions, you might consider avoiding re-inventing the wheel in the platform as some of those concepts are been introduced to both Android and iOS. The recent addition of Android’s Data-Binding Library and ReactiveCocoa for iOS, which might be good options to consider before starting to code.

References

  • An MVVM Pattern introduction - Link.
  • Patterns - WPF Apps With The Model-View-ViewModel Design Pattern - Link.
  • Cross-Platform - Share UI Code Across Mobile Platforms - Link.
  • Android Binding library - Link.
  • ReactiveCocoa documentation - Link.
  • Command Pattern - Link.

Share this post

  

Software Engineer at Microsoft | Former MVP, Certified developer
@stvansolano
More about me

 Browse by tag

 Xamarin

 

 DotNet

 

 Spanish/Español

 

 Productividad

 

 Open Source

 

 Microsoft

 

 AppCenter

 

 DevOps

 

 Conferences

 

 Google

 

 Docker

 

 Kubernetes

 

 XamarinDiplomado

 

 ASP.NET

 

 Web Apps

 

 Yeoman

 

 HTML5

 

 REST

 

 Web API

 

 Meetup

 

 Freelance

 

 Android

 

 Mobile

 

 CodeMentor

 

 MVVM

 

 iOS

 

 OSS

 

 .NET

 

 Github

 

 FSharp

 

 MVP

 

 Comunidad

 

 Azure

 

 AI

 

 Exam

 

 Certification

 

 Rifa

 

 Markdown

 

 GitHub

 

 Backend

 

 Online

 

 WebAPI

 

 MacOS

 

 Asp.Net Core

 

 .Net

 

 Taller

 

 Charla

 

 Git

 

 WordPress

 

 Spanish/English

 

 UX / UI

 

 Realm

 

 Bootcamp

 

 Productivity

 

 VisualStudio

 

 Contenedores

 

 DevDays

 

 Maui

 

 Workshop

 

 XamarinForms

 

 Cloud

 

 NetCore

 

 Serverless

 

 SignalR

 

 CosmosDB

 

 MachineLearning

 

 Python

 

 MVPSummit

 

 YouTube

 

 VS4Mac

 

 containers

 

 Linux

 

 Windows

 

 Career

 

 OpenSource

 

 TestCloud