Book Image

Xamarin: Cross-Platform Mobile Application Development

By : George Taskos, Jonathan Peppers, Can Bilgin
Book Image

Xamarin: Cross-Platform Mobile Application Development

By: George Taskos, Jonathan Peppers, Can Bilgin

Overview of this book

Developing a mobile application for just one platform is becoming a thing of the past. Companies expect their apps to be supported on iOS, Android, and Windows Phone, while leveraging the best native features on all three platforms. The primary goal of this course is to equip you with the knowledge to successfully analyze, develop, and manage Xamarin cross-platform projects using the most efficient, robust, and scalable implementation patterns. Module 1 is a step-by-step guide to building real-world applications for iOS and Android. The module walks you through building a chat application, complete with a backend web service and native features such as GPS location, camera, and push notifications. Additionally, you'll learn how to use external libraries with Xamarin and Xamarin.Forms. Module 2 provide you recipes on how to create an architecture that will be maintainable, extendable, use Xamarin.Forms plugins to boost productivity. We start with a simple creation of a Xamarin.Forms solution, customize the style and behavior of views for each platform. Further on, we demonstrate the power of architecting a cross-platform solution. Next, you will utilize and access hardware features that vary from platform to platform with cross-platform techniques. In the last and the final Module, you will learn about essential tools to leverage the pattern and advanced implementation strategies. We'll show you the toolset for application lifecycle management to help you prepare the development pipeline to manage and see cross-platform projects through to public or private release. After the completion of this course, you will learn a path that will get you up and running with developing cross-platform mobile applications and help you become the go-to person when it comes to Xamarin. This Learning Path combines some of the best that Packt has to offer in one complete, curated package. It includes content from the following Packt products: ?Xamarin Cross-platform Application Development - Second Edition by Jonathan Peppers ?Xamarin Cross-Platform Development Cookbook by George Taskos ?Mastering Cross-Platform Development with Xamarin by Can Bilgin
Table of Contents (6 chapters)

Chapter 11. Xamarin.Forms

Since the beginning of Xamarin's lifetime as a company, their motto has always been to expose the native APIs on iOS and Android directly to C#. This was a great strategy at the beginning, because applications built with Xamarin.iOS or Xamarin.Android were pretty much indistinguishable from a native Objective-C or Java application. Code sharing was generally limited to non-UI code that left a potential gap to fill in the Xamarin ecosystem: a cross-platform UI abstraction. Xamarin.Forms is the solution to this problem, a cross-platform UI framework that renders native controls on each platform. Xamarin.Forms is a great solution for those who know C# (and XAML), but also might not want to get into the full details of using the native iOS and Android APIs.

In this chapter, we will cover the following:

  • Create "Hello World" in Xamarin.Forms
  • Discuss Xamarin.Forms architecture
  • Use XAML with Xamarin.Forms
  • Cover data binding and MVVM with Xamarin.Forms

Creating Hello World in Xamarin.Forms

To understand how a Xamarin.Forms application is put together, let's begin by creating a simple "Hello World" application.

Open Xamarin Studio and perform the following steps:

  • Create a new solution.
  • Navigate to the C# | Mobile Apps section.
  • Create a new Blank App (Xamarin.Forms Portable) solution.
  • Name your solution something appropriate such as HelloForms.

Notice the three new projects that were successfully created: HelloForms, HelloForms.Android, and HelloForms.iOS. In Xamarin.Forms applications, the bulk of your code will be shared, and each platform-specific project is just a small amount of code that starts the Xamarin.Forms framework.

Let's examine the minimal parts of a Xamarin.Forms application:

  • App.cs in the HelloForms PCL library. This class holds the startup page of the Xamarin.Forms application. A simple static method, GetMainPage(), returns the startup page of the application. In the default project template,
  • A ContentPage is created with a single label that will be rendered as a UILabel on iOS and a TextView on Android.
  • MainActivity.cs in the HelloForms.Android Android project. This is the main launcher activity of the Android application. The important part for Xamarin.Forms here is the call to Forms.Init(this, bundle) that initializes the Android-specific portion of the Xamarin.Forms framework. Next is a call to SetPage(App.GetMainPage()) that displays the native version of the main Xamarin.Forms page.
  • AppDelegate.cs in the HelloForms.iOS iOS project. This is very similar to Android, except iOS applications startup via a UIApplicationDelegate class. Forms.Init() will initialize the iOS-specific parts of Xamarin.Forms, while App.GetMainPage().CreateViewController() will generate a native controller that can be used as the RootViewController of the main window of the application.

Go ahead and run the iOS project; you should see something similar to the following screenshot:

Creating Hello World in Xamarin.Forms

If you run the Android project, you will get a UI very similar to the iOS, but using the native Android controls, as shown in the following screenshot:

Creating Hello World in Xamarin.Forms

Tip

Even though not covered in this module, Xamarin.Forms also supports Windows Phone applications. However, a PC running Windows and Visual Studio is required to develop for Windows Phone. If you can get a Xamarin.Forms application working on iOS and Android, then getting a Windows Phone version working should be a piece of cake.

Understanding the architecture behind Xamarin.Forms

Getting started with Xamarin.Forms is very easy, but it is always good to look behind the curtain to understand what is happening behind the scenes. In the earlier chapters of this module, we created a cross-platform application using native iOS and Android APIs directly. Certain applications are much more suited for this development approach, so understanding the difference between a Xamarin.Forms application and a plain Xamarin application is important to know when choosing what framework is best suited for your app.

Xamarin.Forms is an abstraction over the native iOS and Android APIs that you can call directly from C#. So, Xamarin.Forms is using the same APIs you would in a plain Xamarin application, while providing a framework that allows you to define your UIs in a cross-platform way. An abstraction layer such as this is in many ways a very good thing because it gives you the benefit of sharing the code driving your UI as well as any backend C# code that could have also been shared in a standard Xamarin app. The main disadvantage, however, is a slight hit in performance and being limited by the Xamarin.Forms framework as far as what types of controls are available. Xamarin.Forms also gives the option of writing renderers that allow you to override your UI in a platform-specific way. However, in my opinion, renderers are still somewhat limited in what can be achieved.

See the difference in a Xamarin.Forms application and a traditional Xamarin app in the following diagram:

Understanding the architecture behind Xamarin.Forms

In both the applications, the business logic and backend code of the application can be shared, but Xamarin.Forms gives you an enormous benefit by allowing your UI code to be shared as well.

Additionally, Xamarin.Forms applications have two project templates to choose from, so let's cover each option:

  • Xamarin.Forms Shared: This creates a shared project with all of your Xamarin.Forms code, an iOS project, and an Android project.
  • Xamarin.Forms Portable: This creates a portable class library that contains all the shared Xamarin.Forms code, an iOS project, and an Android project.

In general, both the options will work fine for any application. Shared projects are basically a collection of code files that get added automatically to another project referencing it. Using a shared project allows you to use preprocessor statements to implement platform-specific code. Portable class library projects, on the other hand, create a portable .NET assembly that can be used on iOS, Android, and various other platforms. PCLs can't use preprocessor statements, so you generally set up platform-specific code with an interface or abstract/base classes. In most cases, I think a portable class library is a better option since it inherently encourages better programming practices. You can refer to Chapter 3, Code Sharing between iOS and Android, for details on the advantages and disadvantages of these two code-sharing techniques.

Using XAML in Xamarin.Forms

In addition to defining Xamarin.Forms controls from C# code, Xamarin has provided the tool to develop your UI in Extensible Application Markup Language (XAML). XAML is a declarative language that is basically a set of XML elements that map to a certain control in the Xamarin.Forms framework. Using XAML is comparable to what you would think of using HTML to define the UI on a web page, with the exception that XAML in Xamarin.Forms creates C# objects that represent a native UI.

To understand how XAML works in Xamarin.Forms, let's create a new page with lots of UI on it:

  1. Create a new Xamarin.Forms Portable solution by navigating to C# | Mobile Apps | Blank App (Xamarin.Forms Portable).
  2. Name the project something appropriate such as UIDemo.
  3. Add a new file by navigating to the Forms | Forms ContentPage XAML item template. Name the page UIDemoPage.
  4. Open UIDemoPage.xaml.

Now, let's edit the XAML code. Add the following XAML code between the <ContentPage.Content> tag:

<StackLayout Orientation="Vertical" Padding="10,20,10,10"> <Label Text="My Label" XAlign="Center" /> <Button Text="My Button" /> <Entry Text="My Entry" /> <Image Source="xamagon.png" /> <Switch IsToggled="true" /> <Stepper Value="10" /> </StackLayout>

Go ahead and run the application on iOS and Android. Your application will look something like the following screenshot:

Using XAML in Xamarin.Forms

Then, on Android, Xamarin.Forms will render the screen in the same way, but with the native Android controls:

Using XAML in Xamarin.Forms

First, we created a StackLayout control, which is a container for other controls. It can layout controls either vertically or horizontally one by one as defined by the Orientation value. We also applied a padding of 10 around the sides and bottom, and 20 from the top to adjust the iOS status bar. You might be familiar with this syntax for defining rectangles if you are familiar with WPF or Silverlight. Xamarin.Forms uses the same syntax of the left, top, right, and bottom values delimited by commas.

We also used several of the built-in Xamarin.Forms controls to see how they work:

  1. Label: We used this earlier in the chapter. This is used only to display the text. This maps to a UILabel on iOS and a TextView on Android.
  2. Button: This is a general purpose button that can be tapped by a user. This control maps to a UIButton on iOS and a Button on Android.
  3. Entry: This control is a single-line text entry. It maps to a UITextField on iOS and an EditText on Android.
  4. Image: This is a simple control to display an image on the screen, which maps to a UIImage on iOS and an ImageView on Android. We used the Source property of this control that loads an image from the Resources folder on iOS and the Resources/drawable folder on Android. You can also set URLs on this property, but it is best to include the image in your project for the performance.
  5. Switch: This is an on/off switch or a toggle button. It maps to a UISwitch on iOS and a Switch on Android.
  6. Stepper: This is a general-purpose input to enter numbers via two plus and minus buttons. On iOS, this maps to a UIStepper, while on Android Xamarin.Forms implements this functionality with two Button.

This is just some of the controls provided by Xamarin.Forms. There are also more complicated controls such as the ListView and TableView you would expect to develop mobile UIs.

Even though we used XAML in this example, you can also implement this Xamarin.Forms page from C#. Here is an example of what this would look like:

public class UIDemoPageFromCode : ContentPage
{
  public UIDemoPageFromCode()
  {
    var layout = new StackLayout 
    {
      Orientation = StackOrientation.Vertical,
      Padding = new Thickness(10, 20, 10, 10),
    };

    layout.Children.Add(new Label 
    {
      Text = "My Label",
      XAlign = TextAlignment.Center,
    });

    layout.Children.Add(new Button 
    {
      Text ="My Button",
    });

    layout.Children.Add(new Image 
    {
      Source = "xamagon.png",
    });

    layout.Children.Add(new Switch 
    {
      IsToggled = true,
    });

    layout.Children.Add(new Stepper 
    {
      Value = 10,
    });

    Content = layout;
  }
}

So you can see that using XAML can be a bit more readable and is generally a bit better at declaring UIs. However, using C# to define your UIs is still a viable, straightforward approach.

Using data binding and MVVM

At this point, you should be grasping the basics of Xamarin.Forms, but you may be wondering how the MVVM design pattern fits into the picture. The MVVM design pattern was originally conceived for its use along with XAML and the powerful data binding features XAML provides, so it is only natural that it is a perfect design pattern to be used with Xamarin.Forms.

Let's cover the basics of how data binding and MVVM is set up with Xamarin.Forms:

  1. Your Model and ViewModel layers will remain mostly unchanged from the MVVM pattern we covered earlier in the module.
  2. Your ViewModel layer should implement the INotifyPropertyChanged interface, which facilitates data binding. To simplify things in Xamarin.Forms, you can use the BindableObject base class and call OnPropertyChanged when the values change on your ViewModel.
  3. Any page or control in Xamarin.Forms has a BindingContext property, which is the object that it is data bound to. In general, you can set a corresponding ViewModel to each view's BindingContext property.
  4. In XAML, you can set up data binding using the syntax of the form Text="{Binding Name}". This example will bind the Text property of the control to a Name property of the object residing in the BindingContext.
  5. In conjunction with data binding, events can be translated to commands using the ICommand interface. So, for example, a button's click event can be data bound to a command exposed by a ViewModel. There is a built-in Command class in Xamarin.Forms to support this.

Tip

Data binding can also be set up from C# code in Xamarin.Forms via the Binding class. However, it is generally much easier to set up bindings from XAML, since the syntax has been simplified there.

Now that we have covered the basics, let's go through it step by step and partially convert our XamChat sample application discussed earlier in the module to use Xamarin.Forms. For the most part, we can reuse most of the Model and ViewModel layers, although we will have to make a few minor changes to support data binding from XAML.

Let's begin by creating a new Xamarin.Forms application backed by a PCL named XamChat:

  1. First, create three folders in the XamChat project named Views, ViewModels, and Models.
  2. Add the appropriate ViewModels and Models classes from the XamChat application in the earlier chapter. These are found in the XamChat.Core project.
  3. Build the project and just make sure that everything is saved. You will get a few compiler errors that we will resolve shortly.

The first class that we will need to edit is the BaseViewModel class. Open it and make the following changes:

public class BaseViewModel : BindableObject
{
  protected readonly IWebService service = DependencyService.Get<IWebService>();
  protected readonly ISettings settings = DependencyService.Get<ISettings>();

  private bool isBusy = false;
  public bool IsBusy
  {get {return isBusy;}
      set {isBusy = value; OnPropertyChanged();}}
}

First of all, we removed the calls to the ServiceContainer class, because Xamarin.Forms provides its own IoC container called DependencyService. It functions very similar to the container we built in the previous chapters, except that it only has one method, Get<T>, and the registrations are set up via an assembly attribute that we will set up shortly.

Additionally, we removed the IsBusyChanged event in favor of the INotifyPropertyChanged interface that supports data binding. Inheriting from BindableObject gives us the helper method, OnPropertyChanged, which we use to inform bindings that the value has changed in Xamarin.Forms. Notice that we didn't pass string, which contains the property name, to OnPropertyChanged. This method uses a lesser-known feature of .NET 4.0 called CallerMemberName, which will automatically fill in the calling property's name at runtime.

Next, let's set up our required services with DependencyService. Open App.cs in the root of the PCL project, and add the following two lines above the namespace declaration:

[assembly: Dependency(typeof(XamChat.Core.FakeWebService))]
[assembly: Dependency(typeof(XamChat.Core.FakeSettings))]

DependencyService will automatically pick up these attributes and inspect the types that we declared. Any interfaces that these types implement will be returned for any future callers of DependencyService.Get<T>. I normally put all Dependency declarations in the App.cs file so that they are easy to manage and in one place.

Next, let's modify LoginViewModel by adding a new property:

public Command LoginCommand { get; set; }

We'll use this shortly to data bind a button's command. One last change in the View Model layer is to set up INotifyPropertyChanged for the MessageViewModel:

Conversation[] conversations;

public Conversation[] Conversations
{get {return conversations; }
  set {conversations = value; OnPropertyChanged();}
}

Likewise, you can repeat this pattern for the remaining public properties throughout the ViewModel layer, but this is all that we will need for this example. Next, let's create a new Foms ContentPage Xaml item under the Views folder named LoginPage. In the code-behind file LoginPage.xaml.cs, we'll just need to make a few changes:

public partial class LoginPage : ContentPage
{
  readonly LoginViewModel loginViewModel = new LoginViewModel();

  public LoginPage()
  {
    Title = "XamChat";
    BindingContext = loginViewModel;

    loginViewModel.LoginCommand = new Command(async () =>
    {
      try
      {
        await loginViewModel.Login();

        await Navigation.PushAsync(new ConversationsPage());
      }
      catch (Exception exc)
      {
        await DisplayAlert("Oops!", exc.Message, "Ok");
      }
    });

    InitializeComponent();
  }
}

We did a few important things here, including setting the BindingContext to our LoginViewModel. We set up LoginCommand, which basically invokes the Login method and displays a message if something goes wrong. It also navigates to a new page if successful. We also set the title, which will show up in the top navigation bar of the application.

Next, open LoginPage.xaml, and we'll add the following XAML code inside the content page's content:

<StackLayout Orientation="Vertical" Padding="10,10,10,10">
  <Entry Placeholder="Username" Text="{Binding Username}" />
  <Entry Placeholder="Password" Text="{Binding Password}" IsPassword="true" />
  <Button Text="Login" Command="{Binding LoginCommand}" />
  <ActivityIndicator IsVisible="{Binding IsBusy}" IsRunning="true" />
</StackLayout>

This will set up the basics of two text fields, a button, and a spinner complete with all the bindings to make everything work. Since we set up the BindingContext from the LoginPage code behind, all the properties are bound to the LoginViewModel.

Next, create ConversationsPage as a XAML page as we did earlier, and edit the ConversationsPage.xaml.cs code behind:

public partial class ConversationsPage : ContentPage
{
  readonly MessageViewModel messageViewModel = new MessageViewModel();

  public ConversationsPage()
  {
    Title = "Conversations";
    BindingContext = messageViewModel;

    InitializeComponent ();

    Appearing += async (sender, e) => 
    {
      try
      {
        await messageViewModel.GetConversations();
      }
      catch (Exception exc)
      {
        await DisplayAlert("Oops!", exc.Message, "Ok");
      }
    };
  }
}

In this case, we repeated a lot of the same steps. The exception is that we used the Appearing event as a way to load the conversations to display on the screen.

Now let's add the following XAML code to ConversationsPage.xaml:

<ListView ItemsSource="{Binding Conversations}">
  <ListView.ItemTemplate>
    <DataTemplate>
      <TextCell Text="{Binding Username}" />
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

In this example, we used ListView to data bind a list of items and display on the screen. We defined a DataTemplate class that represents a set of cells for each item in the list that ItemsSource is data bound to. In our case, a TextCell displaying the Username is created for each item in the Conversations list.

Last but not least, we must return to the App.cs file and modify the startup page:

public static Page GetMainPage()
{
  return new NavigationPage(new LoginPage());
}

We used NavigationPage here so that Xamarin.Forms can push and pop between different pages. This uses a UINavigationController on iOS so you can see how the native APIs are being used on each platform.

At this point, if you compile and run the application, you will get a functional iOS and an Android application that can login and view a list of conversations:

Using data binding and MVVM

Summary

In this chapter, we covered the basics of Xamarin.Forms and learned how it can be very useful to build your own cross-platform applications. Xamarin.Forms shines for certain types of apps, but can be limiting if you need to write more complicated UIs or take advantage of native drawing APIs. We discovered how to use XAML to declare our Xamarin.Forms UIs and understood how Xamarin.Forms controls are rendered on each platform. We also dived into the concepts of data binding and discovered how to use the MVVM design pattern with Xamarin.Forms. Last but not least, we began porting the XamChat application that was discussed earlier in the module to Xamarin.Forms and we were able to reuse most of the backend code.