1. Setting the Stage
Ever since Xamarin came onto the stage several years ago, C# developers have been delighting in being able to create mobile applications that target non-Microsoft platforms. It's with quite a bit of excitement that we can now write apps in a language that is very familiar to us, and be able to distribute them to the big players in the mobile space, iOS and Android.
Since these apps can be written in a single language, it only makes sense to want to write a single code base that would compile into separate apps for the different platforms and ultimately allow us to reach the world of Write Once, Run Everywhere/Anywhere. This sounds good on paper, but it wasn't quite the case.
2. The Problem
The concept of writing an application a single time and being able to run it on several platforms is not a new one. Developers have been attempting to accomplish this for many years with desktop and server applications written in Java or C/C++, and compiled to the specific platforms. Why should mobile apps be any different? The simple answer is, they're not. As mobile app developers, we run into the same major cross-platform issues that others do, the user interface.
When it comes to writing any sort of software that you want to be able to run on different platforms, the easy part is the logic. By easy, I mean the part of the application that doesn't change regardless of the platform you're targeting. No matter what platform you're targeting, you'll still need to get data from services, databases, files, etc. Odds are that this will very rarely change. The part that does change is the UI. This is primarily due to the inherent differences of the various platforms. Each platform will have their own SDK, defining the platform's capabilities as well as their visual components and characteristics.
When looking at today's mobile landscape, you'll quickly find that there are three big players, iOS, Android, and Windows Phone. These three are in constant competition to try to one-up the others to gain more market share. But, for the foreseeable future, these are the platforms that you'll want to be targeting. The reason that cross-platform development on mobile can be difficult is due to the fundamental differences in terms of UI. A button is no longer just a button. A button is a UIButton
with XML on iOS, a Button
with AXML on Android, and a Button
with XAML on Windows Phone.
3. The Workaround
As with most cross-platform frameworks, Xamarin has typically had a similar strategy when it comes to limiting the headache that the UI causes, split out the logic from the user interface.
In the world of C#, this strategy will typically leave you with at least N+1 projects within your solution. You will have at least one project for each of the N platforms you are targeting and you will also need to have at least one project that will contain all the shared code that the application relies on. That way, you can have the platform-specific projects simply reference the shared code project and only have to worry about how to visualize the data. Thanks to some new functionality in the .NET and Mono frameworks, you have a couple of options.
Option 1: Portable Class Libraries
Portable Class Libraries or PCLs came on the scene first. It's easiest to think of PCLs as normal class libraries, just with limited functionality. It's easier to understand this concept with an example. Start by creating a new solution/project and name it PCLDemo and select Class Library (Portable) as your project template.
When you click OK in Visual Studio, you'll see another window show up that looks similar to this:
Depending on the tools you have installed, it may look slightly different. In this screen, you have the ability to target as many or as few platforms as you wish. The rule of thumb here is to only choose the platforms that you absolutely need to target. The reason for this is that the more platforms you wish to target, the fewer features will be included in this library. For an up to date list of features supported by the different platforms, check out the Cross Platform Development with the Portable Class Library page on MSDN.
If you don't select the proper platforms in this dialog, you could run into incompatibility issues later. To see this in action, uncheck the bottom three options:
- Windows Phone 8.1
- Xamarin.Android
- Xamarin.iOS
Now, create a new project of one of the types that you removed support from in the PCL. As an example, you could create a new Xamarin.Android project using any of the supplied templates. Once the project is created, right-click on References and select Add Reference. In the Reference Manager dialog, select Solution > Projects from the filter on the left and check the box next to the PCLDemo project.
When you click OK, you'll see an error like the following:
This error lets you know that the project you're currently in is incompatible with the targets available in the selected PCL. You can work around this by going into the PCL project properties and changing the targets. But remember, the more targets, the fewer the supported features.
Advantages of Portable Class Libraries
Even though there are a few things you need to watch out for when using PCLs, there are some very nice advantages.
- non-platform-specific code can be isolated in a separate project(s)
- unit testing of shared code is isolated in a separate project(s)
- shared code in PCLs can be shared with other applications via a compiled dll
Disadvantages of Portable Class Libraries
When choosing a new technology or platform to use, it also pays to understand the disadvantages that need to be addressed.
- feature sets are limited based on the targeted platforms
- PCLs can only add references to other PCLs that share the same target platforms
- PCLs can't contain any platform-specific code
If PCLs seem too limiting to you as a choice for shared code, there is another option.
Option 2: Shared Projects
In the recent release of Visual Studio 2013 Update 2, Microsoft added support for Shared Projects. Shared Projects are another way to share code between different platforms, in some ways similar to PCLs and in other ways drastically different. Let's start by creating a simple example.
Begin by creating another solution/project in Visual Studio and giving it a name of SharedDemo and selecting Shared Project (Empty) as your template.
If you don't see Shared Project (Empty) as an option, make sure that you have the latest version of Visual Studio 2013 Update 2. If you are using the latest version and still don't see it, you may need to install the Shared Project Reference Manager extension.
Once you click OK, Visual Studio will create a new project for you in the Solution Explorer, but it will look a little different than you're used to.
It seems a little lonely, huh? There's no Properties folder and no References folder. What's going on? That's the beauty and often the source of confusion for most developers. By default, a Shared Project doesn't really contain access to any functionality at all. There's some magic going on behind the scenes in Visual Studio/Xamarin Studio, but you will see that shortly.
Another strange feature of a Shared Project is that you can't actually build it. If you were to right-click the solution or project, you wouldn't see any options for building, running, or debugging this project. To understand why this is, you need to take a step back for a moment.
What's the purpose of building a project? The purpose is to transform your code from text into an actual file that can be used by the system you're running on or another application. That is why you can't actually build a Shared Project, no output file is generated by it. This specialized project has an entirely different purpose.
When you create a Shared Project and write code in it, you can add references to it from other projects as much as you want. There are no platforms to target or rules to follow. This is the first differentiator from PCLs. Let's continue with an example. Create a new Xamarin.Android and/or Xamarin.iOS project in your solution and name them appropriately, SharedDemo.Android and/or SharedDemo.iOS.
Now that you have a new project, right-click the References folder and select Add Shared Project Reference. If you don't see this as an option, you may need to review adding the Shared Project Reference Manager extension.
From here, click OK and you should see something similar to this:
This is where the magic comes into play. The code contained with a Shared Project will be compiled into any projects that reference it. That is why you can't build them directly, they need something to reference them before they can be built and compiled. The fact that they can be added to any project as a reference is part of the reason they are such good candidates for cross-platform development.
The other reason they are good candidates for cross-platform development is their support for compiler directives. You can place checks for special directives within your Shared Projects so that only specific platforms get certain functionality or that things are handled differently on different platforms. The following directives can be found in platform projects:
__MOBILE__
Xamarin.Android and Xamarin.iOS projects__IOS__
Xamarin.iOS projects__ANDROID__
Xamarin.Android projects__ANDROID_xx__
Xamarin.Android projects wherexx
is replaced by the targeted Android API versionWINDOWS_PHONE
Windows Phone projectsSILVERLIGHT
Windows Phone projects
You can use these directives inside #ifdef
blocks to check which platform you're running on and use conditional compilation to only include what's needed in each project. This is all done for you by the compiler. Let's see an example.
In your SharedDemo project, add a new class named Recipe and replace the default implementation with the following:
public class Recipe { public string ShortName { get; set; } #if __iOS__ public string LongName { get; set; } #endif public List<string> Ingredients { get; set; } public List<string> Directions { get; set; } }
This is a bit of a contrived example, but it serves to illustrate a simple point. Since I am referencing this code from a Xamarin.Android project, the Recipe class will only contain 3 properties, ShortName
, Ingredients
, and Directions
. If this code was referenced by a Xamarin.iOS project, it would also contain the LongName
property.
You're more likely to use these directives in situations where you run into platform divergence. Common areas of divergence on these mobile platforms have to do with accessing hardware, the file system, etc. You can now place platform code in the Shared Project divided by the directives and Visual Studio will handle the platform-specific compilation to the proper projects. Very nice.
Advantages of Shared Projects
Shared Projects contain some very nice features that assist in the creation of cross-platform projects.
- non-platform and platform-specific code can exist in one project
- have access to libraries and features of parent project
- support directives for separating platform-specific code
Disadvantages of Shared Projects
While Shared Projects have nice features, there are a few not so desirable characteristics.
- no dll is created, so code can't be shared in other solutions
- only have access to libraries in parent project, so non-platform-specific unit test projects can be difficult
- can't reference other projects or libraries
4. Enter Xamarin.Forms
You're now aware of the basic strategies for sharing your app logic. It's time to start looking at your app's user interface. In the most recent release of version 3 of Xamarin, one of the most exciting features is the introduction of Xamarin.Forms. Xamarin.Forms is a library that contains abstractions of typical UI controls that comprise your app's UI. When these controls are used on the specific platforms, they are mapped to render native UI controls so that you get the look and feel of native components.
The basic structure of your cross-platform application doesn't change. You will still be following the N+1 rule as far as projects are concerned, but you'll now be moving your platform-specific UI code from the platform-specific projects into the Shared Projects. Before we get too ahead of ourselves, we need to understand the needs and requirements of not only running, but developing Xamarin.Forms apps.
Requirements
There are a couple of requirements if you want to take full advantage of Xamarin.Forms. To use Xamarin.Forms, your app must be targeting the following platforms:
- iOS 6.1+
- Android 4.0+
- Windows Phone 8
This means that if you plan to target some of the older versions of these platforms, you won't be able to use Xamarin.Forms. In addition to target platform restrictions, there are also a few development system requirements.
OS X
- Xamarin 5 is required to use Xamarin.Forms on OS X.
- To develop iOS apps, Xcode and OS X 10.8+ is needed.
- Windows Phone apps can only be developed on a Windows machine.
Windows
- You'll need Visual Studio 2012 or newer.
- To use the PCL version of Xamarin.Forms, you need to use PCL Profile 78, which includes support for .NET Framework 4.5+, Windows Phone 8 or later, Windows 8, Windows Phone Silverlight 8, Windows Store apps (Windows 8), Xamarin.Android, and Xamarin.iOS.
- It speaks for itself that you'll also need the Windows Phone SDK.
- To develop iOS apps, you'll need Xcode and a Mac running OS X. This is a requirement for all Xamarin.iOS apps created using a Windows machine.
Once you have everything downloaded and configured, you are ready to get started.
5. Building Blocks
The basic idea behind Xamarin.Forms is simple, you refer to common UI controls in the same way regardless of the platforms that you're targeting. A button is a button is a button. You'll create these controls and give them visual characteristics and functionality. You'll then use a Layout
to position the controls on the screen and display everything on a Page
. If you're used to using platform-specific terminology to describe the UI components of your apps, getting used to the new terms may take some time. Luckily, there are only a few concepts to understand.
Pages
In Xamarin.Forms, Pages represent the actual screens of your application. With respect to other platforms, a Page
is an Activity
on Android, a UIViewController
on iOS, and a Page
on Windows Phone. The Page
is the main object that you will be dealing with when you're working with transitions and navigation. Also, a Page
contains only a single child, which in most situations is some type of Layout
.
Layouts
Layouts are controls used to organize UI components on your Page
in a logical way. Within a Layout
, you can set characteristics of the UI controls, such as position and size. You can add any number of UI components to a layout including controls and other Layout
objects.
Views
In Xamarin.Forms, Views are what you would commonly refer to as controls. Common View
objects in Xamarin.Forms are Button
, Label
, and ListView
, to name a few. The Layout
class actually inherits from the View
class, which means that they can be treated the same way and added to Layout
objects.
Cells
Cells are specialized UI components used to customize what would typically be referred to as rows within ListView
and UITableView
objects. There are pre-built Cell
types to add a number of different customizations to your View
objects.
6. Creating a Basic Application
To create a basic Xamarin.Forms application, open Visual Studio, select File > New Project, select the Mobile Apps category, and choose the appropriate template. I've chosen the Blank App (Xamarin.Forms Shared) template. This will create a Xamarin.Forms application using the Shared Project template.
After clicking OK, your solution will be created with four projects. As discussed before, you will have three platform-specific projects and one Shared Project. The Shared Project contains the application logic as well as all the Xamarin.Forms code that will be in charge of creating the screens (Page
objects) of your application.
If you open the SampleFormsApp project, you will see a single class named App.cs. This is the main file of the project that contains the Page
definitions that will ultimately appear on the target platforms. It's a simple class that only contains the following:
public class App { public static Page GetMainPage() { return new ContentPage { Content = new Label { Text = "Hello, Forms !", VerticalOptions = LayoutOptions.CenterAndExpand, HorizontalOptions = LayoutOptions.CenterAndExpand, }, }; } }
The class App
is quite simple in that it only contains a single static method that returns a ContentPage
object. A ContentPage
object is the most basic, no-frills Page
that you can create. It's easiest to think of a ContentPage
as a blank canvas that you can do anything you want with. There is very little functionality built into it.
You'll typically find that most Page
objects have a Content
property that allows you to assign View
object to it. In this case, it's a simple Label
view that prints "Hello, Forms !" to the screen while giving it some basic formatting for centering it.
To get this ContentPage
to load on every platform that we're targeting, there's some custom platform-specific code that needs to be included in each of the platform projects. Let's take a look at the iOS project first. Open the SampleFormsApp.iOS project and take a look at the AppDelegate.cs file. This is the file that contains the wiring into the Xamarin.Forms world.
public override bool FinishedLaunching(UIApplication app, NSDictionary options) { Forms.Init(); window = new UIWindow(UIScreen.MainScreen.Bounds); window.RootViewController = App.GetMainPage().CreateViewController(); window.MakeKeyAndVisible(); return true; }
First, we initialize the Xamarin.Forms library with by invoking Forms.Init()
. After that, the only custom code is setting the window.RootViewController
property to the result of the App.GetMainPage().CreateViewController()
call. That line will launch into the Xamarin.Forms world and everything from that point continues to use Xamarin.Forms.
The process for initializing Xamarin.Forms in Android and Windows Phone is very similar. You will call the Init
method and set the initial Page
to be the result of the App.GetMainPage()
method. This is what it looks like in the SampleFormsApp.Android project in the MainActivity.OnCreate
method.
protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); Xamarin.Forms.Forms.Init(this, bundle); SetPage(App.GetMainPage()); }
And in the SampleFormsApp.WinPhone project in the MainPage
constructor of the partial class MainPage
:
public MainPage() { InitializeComponent(); Forms.Init(); Content = SampleFormsApp.App.GetMainPage().ConvertPageToUIElement(this); }
Conclusion
You should now have a basic understanding of Xamarin.Forms as well as what you can accomplish using them. The journey into this amazing technology doesn't stop there. You will have another choice ahead of you when using Xamarin.Forms, which has to do with layout. And that is precisely what will be covered in the next tutorial.