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 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.
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.
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.
The table below shows which APIs are available on which platforms:
Feature | .NET Framework | Windows Store Apps | Silverlight | Windows Phone | Xamarin |
---|---|---|---|---|---|
Core | Y | Y | Y | Y | Y |
LINQ | Y | Y | Y | Y | Y |
IQueryable | Y | Y | Y | 7.5+ | Y |
Serialization | Y | Y | Y | Y | Y |
Data Annotations | 4.0.3+ | Y | Y | Y | Y |
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 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:
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:
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:
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.
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.
About the author
Emran BajramiSource: toptal.com
Great post I must say and thanks for the information.Durbin Labs provides Mobile and Web Application and Software Development Solution
ReplyDeleteMore info you can vist Cross-Platform App
This comment has been removed by the author.
ReplyDelete
ReplyDeleteHey,
This is really very helpful information.
Thanks for sharing!!
Hire Xamarin Developer
Thanks for sharing such beautiful information with us.Applications need operating systems to run on our devices. There are multiple OS that require different platform for the app to be developed for OS compatibility. But creating app separately for separate OS is time consuming g and costly. To build an app that is compatible with different OS, in this case Android and iOS our developers use cross platform application development tools. You can use HTML CSS or Java scripts and place a device native rapper around it to give a native look and feel without coding twice. The app development is much faster and cheaper. It is a win win for developers and clients.
ReplyDeleteMore info Cross-Platform App
Great blog, Mobile app architecture can be understood as a set of techniques and patterns that are required to follow to develop a well structured mobile application. Because it is the backbone of any mobile app and offers applications with outstanding features, functionality and user experience.
ReplyDeleteI’m happy I located this blog! From time to time, students want to cognitive the keys of productive literary essays composing. Your first-class knowledge about this good post can become a proper basis for such people. nice one קבלן שלד מחיר
ReplyDeleteOakley Titanium sunglasses - Tioga Brands
ReplyDeleteOakley Titanium titanium damascus knives sunglasses · Oakley Titanium mens titanium earrings sunglasses · Oakley Titanium sunglasses nier titanium alloy · Oakley Titanium sunglasses · Oakley Titanium sunglasses · Oakley Titanium titanium scrap price sunglasses 슬롯 가입 머니 · Oakley
Great post! Looking forward to reading more posts of yours. You may visit an article related to spacebar counter the different time intervals that will allow you to know your speed as well as your endurance over various duration of time using the counter. Please comment with your thought after reading the article. Thank you!
ReplyDelete