Mobile13 minute read

Building Cross-platform Apps with Xamarin: Perspective of an Android Developer

Writing reusable code that can be shared across multiple platforms can make developing mobile applications a lot easier. But, how do you do that without paying the usual cost of maintainability, ease of testing, and poor user experience that comes with cross-platform mobile application development?

In this article, Toptal Freelance Software Engineer Emran Bajrami walks us through Xamarin and shows us techniques for building high-quality cross-platform apps.


Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

Writing reusable code that can be shared across multiple platforms can make developing mobile applications a lot easier. But, how do you do that without paying the usual cost of maintainability, ease of testing, and poor user experience that comes with cross-platform mobile application development?

In this article, Toptal Freelance Software Engineer Emran Bajrami walks us through Xamarin and shows us techniques for building high-quality cross-platform apps.


Toptalauthors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.
Emran Bajrami
Verified Expert in Engineering

Emran is a results-driven, articulate, and analytical Android and Java engineer who can think outside the box.

PREVIOUSLY AT

BMW Group
Share

Writing code once and using it on multiple platforms has been a dream of many software developers. Although this has been possible for some time now, it always came at the cost of maintainability, ease of testing, or even worse, poor user experience.

Developing mobile applications using the native SDK is probably the starting point for all developers who have their roots in the realm of desktop application development. Programming languages would become a barrier for some: If someone were experienced in developing Java desktop or back-end applications, moving to a mobile app development firm and working with Android would feel much easier than starting with Objective-C from scratch for iOS.

I was always skeptical of cross-platform application development. JavaScript-based frameworks like Sencha, Cordova, Titanium, etc. never prove to be a wise choice when performance is important. Lack of APIs and quirky user experience were a given with these frameworks.

But then, I came across Xamarin.

Cross-platform development with Xamarin

In this article, you will learn how you can use Xamarin to share code across multiple platforms without compromising any of the other aspects of mobile application development. The article will focus on Android and iOS in particular, but you can use a similar approach add support for any other platform that Xamarin supports.

What Is Xamarin?

Xamarin is a development platform that allows you to write cross-platform—yet native—applications for iOS, Android, and Windows Phone in C# and .NET.

Xamarin provides C# bindings to native Android and iOS APIs. This gives you the power to use all of Android and iOS’ native user interface, notifications, graphics, animation, and other phone features—all using C#.

Each new release of Android and iOS is matched by Xamarin, with a new release that includes bindings for their new APIs.

Xamarin’s port of .NET includes features such as data types, generics, garbage collection, language-integrated query (LINQ), asynchronous programming patterns, delegates, and a subset of Windows Communication Foundation (WCF). Libraries are managed with a linger to include only the referenced components.

Xamarin.Forms is a layer on top of the other UI bindings and the Windows Phone API, which provides a completely cross-platform user interface library.

The scope of Xamarin

Writing Cross-platform Applications

In order to write cross-platform applications with Xamarin, developers need to choose one of the two available types of projects:

  • Portable Class Library (PCL)
  • Shared Project

The PCL allows you to write code that can be shared among multiple platforms, but with one limitation. Since not all .NET APIs are available on all platforms, with a PCL project, you will be limiting it to run on platforms for which it is targeted.

Xamarin's connections and limitations

The table below shows which APIs are available on which platforms:

Feature.NET FrameworkWindows Store AppsSilverlightWindows PhoneXamarin
CoreYYYYY
LINQYYYYY
IQueryableYYY7.5+Y
SerializationYYYYY
Data Annotations4.0.3+YYYY

During the build process, a PCL is compiled into separate DLLs and loaded by Mono during runtime. A different implementation of the same interface can be provided during runtime.

On the other hand, shared projects give you more control by allowing you to write platform-specific code for each platform you want to support. The code in a shared project can contain compiler directives that will enable or disable sections of code depending on which application project is using the code.

Unlike a PCL, a shared project does not produce any DLL. The code is included directly in the final project.

Giving Structure to Your Cross-platform Code with MvvmCross

Reusable code may save money and time for development teams. However, well-structured code makes life much easier for developers. No one appreciates nicely written bug-free code more than developers.

Xamarin by itself provides a mechanism which makes writing reusable cross-platform code much easier.

Mobile developers are familiar with scenarios where they have to write the same logic twice or more in order to support iOS, Android, and other platforms. But with Xamarin, as explained in the previous chapter, it is easy to reuse code which is written for one platform for some other platforms too.

Where does MvvmCross come into place, then?

MvvmCross, as the name may have hinted, makes it possible to use the MVVM pattern in Xamarin applications. It comes with a bunch of libraries, APIs, and utilities which are really handy in cross-platform application development.

MvvmCross can significantly reduce the amount of boilerplate code that you would have written (sometimes multiple times in different languages) in any other approach to application development.

Structure of an MvvmCross Solution

The MvvmCross community recommends a pretty simple and efficient way of structuring an MvvmCross solution:

<ProjectName>.Core
<ProjectName>.UI.Droid
<ProjectName>.UI.iOS

The Core project in an MvvmCross solution is related to reusable code. The Core project is a Xamarin PCL project where the main focus is reusability.

Any code written in Core should be platform agnostic in the maximum possible way. It should only contain logic which can be reused on all platforms. The Core project must not use any Android or iOS API nor access anything specific to any platform.

The business logic layer, data layer, and back-end communication are all perfect candidates for including in the Core project. Navigation through views hierarchy (activities, fragments, etc.) will be achieved in the Core.

Before continuing, it is necessary to understand one architectural design pattern which is crucial for an understanding of MvvmCross and how it works. As it can be seen from the name, MvvmCross heavily depends on the MVVM pattern.

MVVM is an architectural design pattern which facilitates the separation of graphical user interface from the business logic and back-end data.

How is this pattern used in MvvmCross?

Well, since we want to achieve high reusability of our code, we want to have as much as we can in our Core, which is a PCL project. Since views are the only part of the code that differs from one platform to another, we can’t reuse them across platforms. That part is implemented in the projects related to the platform.

MvvmCross structure

MvvmCross gives us the ability to orchestrate application navigation from the Core using ViewModels.

With the basics and technical details out of the way, let’s get started with Xamarin by creating our very own MvvmCross Core project:

Creating an MvvmCross Core Project

Open Xamarin Studio and create a solution named ToptalExampleSolution:

Creating the solution

Since we are creating a Core project, it is a good idea to stick with the naming convention. Make sure the Core suffix is added to the project name.

In order to get MvvmCross support, it is required to add MvvmCross libraries to our project. To add that we can use built-in support for NuGet in Xamarin Studio.

To add a library right click on the Packages folder and select the Add Packages… option.

In the search field, we can search for MvvmCross, which is going to filter out results related to MvvmCross as shown below:

Filtering results

Clicking on the Add Package button will add it to the project.

With MvvmCross added to our project, we are ready to write our Core code.

Let’s define our first ViewModel. In order to create one, create a hierarchy of folders as follows:

Recommended hierarchy of folders

Here is what each of the folders is about:

  • Models: Domain models which represent real state content
  • Services: A folder which holds our service (business logic, database, etc.)
  • ViewModel: The way we communicate with our models

Our first ViewModel is called FirstViewModel.cs

    public class FirstViewModel : MvxViewModel
    {
        private string _firstName;
        private string _lastName;
        private string _fullName;

        public string FirstName
        {
            get
            {
                return _firstName;
            }
            set
            {
                _lastName = value;
                RaisePropertyChanged();
            }
        }

        public string LastName
        {
            get
            {
                return _lastName;
            }

            set
            {
                _lastName = value;
                RaisePropertyChanged();
            }
        }

        public string FullName
        {
            get
            {
                return _fullName;
            }
            set
            {
                _fullName = value;
                RaisePropertyChanged();
            }
        }

        public IMvxCommand ConcatNameCommand
        {
            get
            {
                return new MvxCommand(() =>
                {
                    FullName = $"{FirstName} {LastName}";
                });
            }

          public IMvxCommand NavigateToSecondViewModelCommand
        {
            get
            {
                return new MvxCommand(() =>
                {
                    ShowViewModel<SecondViewModel>();
                });
            }
        }
    }

Now that we have our first ViewModel, we can create our first view and bind things together.

Android UI

To show content of the ViewModel, we need to create a UI.

The first step to creating an Android UI is to create an Android project in the current solution. To do that, right-click on the solution name and select Add -> Add New Project…. In the wizard, select Android app and make sure that you name your project ToptalExample.UI.Droid.

As described earlier, we now need to add MvvmCross dependencies for Android. To do that, follow the same steps as for Core project for adding NuGet dependencies.

After adding MvvmCross dependencies, it is required to add a reference to our Core project so we can use our code written there. To add a reference to the PCL project, right-click on the References folder and select Edit References… option. On the Projects tab, select the previously created Core project and click OK.

Adding a reference to the PCL project

The next part can be a little tricky to understand.

Now we have to tell MvvmCross how it should set up our application. In order to do that, we have to create a Setup class:

namespace ToptalExample.UI.Droid
{
    public class Setup : MvxAndroidSetup
    {
        public Setup(Context context)
            : base(context)
        {
        }

        protected override IMvxApplication CreateApp()
        {
            return new Core.App();
        }
    }
}

As it can be seen from the class, we are telling MvvmCross to CreateApp based on the Core.App implementation, which is a class defined in Core and shown below:

    public class App : MvxApplication
    {
        public override void Initialize()
        {
            RegisterAppStart(new AppStart());
        }
    }

    public class AppStart : MvxNavigatingObject, IMvxAppStart
    {
        public void Start(object hint = null)
        {
            ShowViewModel<FirstViewModel>();
        }
    }

In the App class, we are creating an instance of AppStart, which is going to show our first ViewModel.

The only remaining thing now is to create an Android Layout file which is going to be bound by MvvmCross:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <EditText
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        local:MvxBind="Text FirstName" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        local:MvxBind="Text LastName" />
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        local:MvxBind="Text FullName" />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        local:MvxBind="Click ConcatNameCommand" />
   <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        local:MvxBind="Click NavigateToSecondViewModelCommand" />
</LinearLayout>

In the layout file, we have bindings which are automatically resolved by MvvmCross. For EditText, we are creating a binding for the Text property, which is going to be a two-way binding. Any change invoked from ViewModel side is going to be automatically reflected on the view and vice-versa.

The View class can be an activity or a fragment. For simplicity, we are using an activity which loads the given layout:

[Activity(Label = "ToptalExample.UI.Droid", Theme = "@style/Theme.AppCompat", MainLauncher = true, Icon = "@mipmap/icon")]
    public class MainActivity : MvxAppCompatActivity<FirstViewModel>
    {
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            SetContentView(Resource.Layout.Main);

        }
    }

For the first button, we have a command binding which means when we click on the button MvvmCross will invoke ContactNameCommand from the ViewModel.

For the second button, we are going to show another ViewModel.

iOS UI

Creating an iOS project is not really different from creating an Android project. You need to follow similar steps for adding a new project, only this time, instead of Android, just create an iOS project. Just make sure that you keep the naming convention consistent.

After adding the iOS project, you need to add dependencies for MvvmCross iOS. Steps are absolutely the same as for Core and Android (right click on References in your iOS project and click Add References…).

Now, as we did for Android, it is required to create a Setup class, which is going to tell MvvmCross how to set up our application.

public class Setup : MvxIosSetup
    {
        public Setup(MvxApplicationDelegate appDelegate, IMvxIosViewPresenter presenter)
            : base(appDelegate, presenter)
        {
        }

        protected override MvvmCross.Core.ViewModels.IMvxApplication CreateApp()
        {
            return new App();
        }
    }

Note that the Setup class now extends MvxIosSetup and, for Android, it was extending MvxAndroidSetup.

One addition here is that we have to change our AppDelegate class.

AppDelegate on iOS is responsible for launching the user interface, so we have to tell how the views are going to be presented on iOS. You can learn more about presenters here.

[Register("AppDelegate")]
    public class AppDelegate : MvxApplicationDelegate
    {
        // class-level declarations

        public override UIWindow Window
        {
            get;
            set;
        }

        public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
        {
            Window = new UIWindow(UIScreen.MainScreen.Bounds);

            var presenter = new MvxIosViewPresenter(this, Window);

            var setup = new Setup(this, presenter);
            setup.Initialize();

            var startup = Mvx.Resolve<IMvxAppStart>();
            startup.Start();

            Window.MakeKeyAndVisible();

            return true;
        }
}

In order to present our VIewModel, we need to create a view. For that case, let’s create a ViewController by right-clicking on the project and selecting Add -> New File and select ViewController from iOS section, which we are going to name FirstViewController.

Xamarin creates three files in which we are going to define what our bindings are going to be. Unlike Android, for iOS, we have to define our bindings in a different way, through code (although we can do that on Android too and, for some cases, it is required to do so).

When it is required to navigate between views, it is done via the ViewModel. In the command NavigateToSecondViewModelCommand, the method ShowViewModel<SecondViewModel>() will find the appropriate view and navigate to it.

But, how does MVVMCross know which view to load?

There isn’t any magic in that. When we create a view for Android (Activity or Fragment) we are extending one of the base classes with type parameters (MvxAppCompatActivity<VM>). When we call ShowViewMolel<VM>, MvvmCross looks up for a View which extends Activity or Fragment class with type parameters VM. This is why you are not allowed to have two view classes for the same ViewModel.

Inversion of Control

Since Xamarin is merely providing C# wrappers around native APIs, it isn’t providing any form of dependency injection (DI) or inversion of control (IoC) mechanism.

Without runtime injection of dependencies or compile time injection, it is not easy to create loosely coupled, reusable, testable, and easily maintainable components. The idea of IoC and DI has been known for a really long time; details about IoC can be found in many articles online. You can learn more about these patterns from Martin Fowler’s introductory article.

IoC has been available since early versions of MvvmCrosses, and it allows injection of dependencies at runtime when the application starts and whenever they are required.

In order to get loosely coupled components, we should never require concrete implementations of classes. Requiring concrete implementations limits the ability to change the behavior of the implementations during runtime (you cannot replace it with another implementation). It makes it difficult to test these components.

For that reason, we are going to declare an interface for which we are going to have one concrete implementation.

public interface IPasswordGeneratorService
{
     string Generate(int length);
}

And implementation:

    public class PasswordGeneratorService : IPasswordGeneratorService
    {
        public string Generate(int length)
        {
            var valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
            var res = new StringBuilder();
            var rnd = new Random();
            while (0 < length--)
            {
                res.Append(valid[rnd.Next(valid.Length)]);
            }
            return res.ToString();
        }
    }

Our ViewModel can now require an instance of the interface IPasswordGenerationService, which we are responsible for providing.

In order for MvvmCross to inject a PasswordGeneratorService implementation at runtime, we need to tell MvvmCross which implementation to use. If we want to use one implementation for both platforms we can register the implementations in App.cs, after application registration:

public override void Initialize()
        {
            RegisterAppStart(new AppStart());

            Mvx.LazyConstructAndRegisterSingleton<IPasswordGeneratorService, PasswordGeneratorService>();
        }

The above call to static method LazyConstructAndRegisterSingleton<TInterface, TType> registers the implementation to be injected. This method registers the appropriate implementation but does not create an object.

The object is created only when it is required and only once since it is registered as a singleton.

If we want to create a singleton object right away, it can be achieved by calling Mvx.RegisterSingleton<TInterface>().

There are cases where we don’t want to have only singletons in our application. Our object may not be thread-safe or there may be some other reason why we want to always have a new instance. If that’s the case, MvvmCross provides method Mvx.RegisterType<TInterface,TType>(), which can be used to register the implementation in a way that instantiates a new instance whenever it is required.

In case you need to provide separate concrete implementations for each platform, you can always do so in the platform-specific projects:

public class DroidPasswodGeneratorService : IPasswordGeneratorService
    {
        public string Generate(int length)
        {
            return "DroidPasswordGenerator";
        }
    }

And the registration of our implementation is done in the Setup.cs class under the Droid project:

protected override void InitializePlatformServices()
        {
            base.InitializePlatformServices();

            Mvx.LazyConstructAndRegisterSingleton<IPasswordGeneratorService, DroidPasswodGeneratorService>();

        }

After initialization of the PCL code, MvvmCross will call InitializePlatformServices and register our platform specific service implementations.

When we register multiple singleton implementations, MvvmCross will use only the implementation which was registered last. All other registrations are going to be discarded.

Build Cross-platform Apps with Xamarin

In this article, you have seen how Xamarin allows you to share code across different platforms and still keep the native feel and performance of the applications.

MvvmCross gives another layer of abstraction further enhancing the experience of building cross-platform applications with Xamarin. The MVVM pattern provides a way to create navigation and user interaction flows that are common for all platforms, making the amount of platform-specific code you need to write limited to views alone.

I hope this article has given you a reason to take a peek into Xamarin and motivated you to build your next cross-platform application with it.

Hire a Toptal expert on this topic.
Hire Now
Emran Bajrami

Emran Bajrami

Verified Expert in Engineering

Sarajevo, Federation of Bosnia and Herzegovina, Bosnia and Herzegovina

Member since January 14, 2016

About the author

Emran is a results-driven, articulate, and analytical Android and Java engineer who can think outside the box.

authors are vetted experts in their fields and write on topics in which they have demonstrated experience. All of our content is peer reviewed and validated by Toptal experts in the same field.

PREVIOUSLY AT

BMW Group

World-class articles, delivered weekly.

By entering your email, you are agreeing to our privacy policy.

World-class articles, delivered weekly.

By entering your email, you are agreeing to our privacy policy.

Join the Toptal® community.