Introduction

Xamarin is a powerful platform allowing the development of mobile apps using a shared codebase written in C#. The unique feature of the platform is the 100% availability of each mobile platform's native APIs. Xamarin covers Windows Phone, Android, and iOS. This post will detail creating an app with a single codebase targeting Android and iOS using Xamarin Forms.

Xamarin Forms is an abstraction of each platform's APIs into a single API allowing for each mobile platform to share a single codebase while maintaining a native look and feel. While there are some limitations to this approach the most commonly used components are contained within Xamarin Forms. Also, in the situations where each platform differs widely you can always create a shared interface with each mobile platform having it's own implementation accessible via the same C# API using Xamarin's DependencyService.

Uses

When I first starting looking into Xamarin I was seeing it as a fast prototyping tool, or something I could use to build a small targeted app for multiple platforms. After looking into it further it seems like it can handle a full experience. Obviously, recommended for C# developers. One downside is you lose some of each platform's unique lifecycle. Building and testing your app now becomes part of the Xamarin lifecycle and you can lose some of those testing tools native developers are used to. Xamarin provides some options for you here however, with Xamarin Test Cloud and Xamarin Insights for real-time analytics.

A practical use case would be to use Xamarin to create a shared model and leave the UI to be implemented natively for each platform. Essentially using Xamarin to manage your model objects and network activity achieving a truly shared model across platforms. The UI code would be part of Android and iOS subprojects, allowing use of each platform's native APIs and components. This is where Xamarin shines, as you have access to the complete set of each platform's API available to you in C#.

The demo application accompanying this post is a simple note taker. You can add notes to a list and delete them later. The storage of the notes will differ for each platform; Android uses SharedPreferences and iOS uses NSUserDefaults. The interesting part here, only a single call will be used from the Xamarin Forms project to accomplish this.

App Setup

Using Xamarin Studio we create a new solution: Blank App(Xamarin.Forms.Shared). On app startup the main page is wrapped in a NavigationPage so we have a title bar for each pushed screen on both platforms. This is equivalent to using an ActionBarActivity on Android or a UINavigationController on iOS.

public App ()
{
 // The root page of your application
 MainPage = new NavigationPage (new MainPage ());
}

App Layout

The app contains a text field at the top to enter the note's contents. Along with a button to add the note to the list below. This layout is achieved using Xamarin's StackLayout in the vertical orientation. The layouts contained within Xamarin Forms closely resemble Android's layouts as they can be defined via XML. The toolbar also comes into use to place a 'Clear Notes' button. Adding a ToolbarItem to the ContentPage.ToolbarItems element will allow the item to appear in the Android action bar and the UINavigation toolbar on iOS.

Utilizing bindings within the page's XAML layout file, the content of the note is bound to a row in the ListView. If the item being bound were more complex than a simple string you would be able to use properties of the item to bind multiple values to different parts of the row's views. Since the item here is a simple string, the Label's text attribute is simple set to {Binding} essentially binding the entire item.
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SharedUIDemo.MainPage" Title="Home">
 <ContentPage.ToolbarItems>
 <ToolbarItem Name="Clear Notes" Clicked="OnClearNotes" Order="Primary" Priority="0"/>
 ContentPage.ToolbarItems>
 <ContentPage.Content>
 <StackLayout Orientation="Vertical">
 <StackLayout Orientation="Horizontal" Padding="10,0,10,0">
 <Entry x:Name="note" HorizontalOptions="FillAndExpand" Placeholder="Enter note text" />
 <Button x:Name="add" Text="Add" />
 StackLayout>
 <ListView x:Name="listView">
 <ListView.ItemTemplate>
 <DataTemplate>
 <ViewCell>
 <ViewCell.ContextActions>
 <MenuItem Clicked="OnDelete" Text="Delete" IsDestructive="True" CommandParameter="{Binding}" />
 ViewCell.ContextActions>
 <StackLayout Orientation="Vertical" Padding="10,0,10,0">
 <Label Text="{Binding}" HorizontalOptions="Fill" LineBreakMode="TailTruncation" />
 StackLayout>
 ViewCell>
 DataTemplate>
 ListView.ItemTemplate>
 ListView>
 StackLayout>
 ContentPage.Content>
ContentPage>

Interactions

When the add button is pressed the text from the text field is copied to the ObservableCollection of notes and the text field is cleared. Using an ObservableCollection will automatically update the ListView when an item is modified.
add.Clicked += (object sender, EventArgs e) => {
 notes.Add (note.Text);
 note.Text = "";
 if (prefs != null) {
 prefs.saveNotes (notes);
 }
};
When a note is clicked a detail page is pushed to display the full contents of the note which are truncated on the main page. The content of the note is passed to the DetailPage constructor and manually set on the page's label. The null check is needed as this same method is called when an item is deselected.
listView.ItemsSource = notes;
listView.ItemSelected += (object sender, SelectedItemChangedEventArgs e) => {
 if (e.SelectedItem == null) return;

 Navigation.PushAsync (new DetailPage (e.SelectedItem.ToString ()));
 listView.SelectedItem = null;
};
Notes can also be individually delete. This is a really nice feature within Xamarin Forms, by simply adding a MenuItem to the ViewCell.ContextActions element you have platform specific actions for each list item. On iOS this menu appears during a swipe left on the row, and by long-pressing on Android. Setting the isDestructive attribute to true gives you the red appearance of the menu item on iOS. The item is then passed to the specified 'OnDelete' method so it can be removed from the list.
<ViewCell.ContextActions>
 <MenuItem Clicked="OnDelete" Text="Delete" IsDestructive="True" CommandParameter="{Binding}" />
ViewCell.ContextActions>
public void OnDelete (object sender, EventArgs e)
{
 var mi = ((MenuItem)sender);
 string item = (string)mi.CommandParameter;
 notes.Remove (item);
}

DependencyService

Xamarin Forms' DependencyService allows you to create an interface in the shared project and leave the implementation up to the individual platforms. For note storage the IPlatformPreferences interface is used. Each platform specific project should contain a class which implements this interface. In the demo application Android uses SharedPreferences and iOS uses NSUserDefaults for storage of the notes.
public interface IPlatformPreferences
{
 Collection<string> getNotes ();
 void saveNotes (Collection<string> notes);
 void clearNotes ();
}
To use the DependencyService you simply call the static 'Get' method specifying the interface type. If the interface has an implementation on that platform it will be returned, otherwise it returns null. Should the DependencyService return null for any reason the app is guarded against this and simply is unable to load notes or save notes to storage but still allowed to function. This is helpful when a feature is only available on a single platform, or when the implementations need to differ slightly like the demo app.
public partial class MainPage : ContentPage
{
 ObservableCollection<string> notes;
 IPlatformPreferences prefs;

 public MainPage ()
 {
 InitializeComponent ();
 prefs = DependencyService.Get<IPlatformPreferences> ();
 if (prefs == null) {
 notes = new ObservableCollection<string> ();
 } else {
 notes = new ObservableCollection<string> (prefs.getNotes ());
 }}
...
}
One thing to note here is, each platform does not have to store notes in the same format. On Android, notes are saved as delimited strings while on iOS they're stored as an array of strings.
[assembly: Dependency (typeof(Preferences))]
namespace SharedUIDemo.iOS
{
 public class Preferences : IPlatformPreferences
 {
 private const string NOTES = "NOTES";

 public Collection<string> getNotes ()
 {
 Collection<string> notes = new Collection<string> ();
 NSObject[] arrayNotes = NSUserDefaults.StandardUserDefaults.ArrayForKey (NOTES);
 if (arrayNotes != null) {
 foreach (NSObject arrayNote in arrayNotes) {
 notes.Add (arrayNote.ToString ());
 }
 }

 return notes;
 }

 public void saveNotes (Collection<string> notes)
 {
 NSMutableArray arrayNotes = new NSMutableArray ();
 for (int i = 0; i < notes.Count; i++) {
 arrayNotes.Add (new NSString (notes [i]));
 }
 NSUserDefaults.StandardUserDefaults.SetValueForKey (arrayNotes, new NSString (NOTES));
 NSUserDefaults.StandardUserDefaults.Synchronize ();
 }

 public void clearNotes ()
 {
 NSUserDefaults.StandardUserDefaults.RemoveObject (NOTES);
 NSUserDefaults.StandardUserDefaults.Synchronize ();
 }
 }
}
[assembly: Dependency (typeof(Preferences))]
namespace SharedUIDemo.Droid
{
 public class Preferences : IPlatformPreferences
 {
 private const string PREFS_NAME = "SharedUIDemo";
 private const string NOTES = "NOTES";

 private ISharedPreferences getPreferences ()
 {
 Context context = MainActivity.APPLICATION_CONTEXT;
 return context.GetSharedPreferences (PREFS_NAME, FileCreationMode.Private);
 }

 public Collection<string> getNotes ()
 {
 ISharedPreferences preferences = getPreferences ();
 string prefNotes = preferences.GetString (NOTES, "");
 Collection<string> notes = new Collection<string> ();

 string[] arrayNotes = prefNotes.Split (new char[]{ '|' }, StringSplitOptions.RemoveEmptyEntries);
 foreach (string note in arrayNotes) {
 notes.Add (note);
 }

 return notes;
 }

 public void saveNotes (Collection<string> notes)
 {
 string prefNotes = "";
 foreach (string note in notes) {
 prefNotes += note + "|";
 }

 ISharedPreferences preferences = getPreferences ();
 ISharedPreferencesEditor editor = preferences.Edit ();
 editor.PutString (NOTES, prefNotes);
 editor.Commit ();
 }

 public void clearNotes ()
 {
 ISharedPreferences preferences = getPreferences ();
 ISharedPreferencesEditor editor = preferences.Edit ();
 editor.Remove (NOTES);
 editor.Commit ();
 }
 }
}

Conclusion

If you are a C# developer you now have a capable tool to build beautiful cross platform applications. If you are able to strictly use Xamarin Forms this process is a breeze. When it comes to the nuances of each mobile platform the differences will cause you to dig into each platform's APIs for solutions, though Xamarin has you covered with great documentation. When it comes to platform specific implementations you can be assured you'll be able to use solutions used by that platform's developers with no problems with 100% of the same API calls. The source code for the demo described can be found at https://github.com/antoinecampbell/xamarin-shared-ui-demo.