Keywords

These keywords were added by machine and not by the authors. This process is experimental and the keywords may be updated as the learning algorithm improves.

At the outset of this book, I mentioned the unicorn of mobile development: write once, deploy anywhere. In the quest for this unicorn, the fair maiden that might entice this beast to appear is cross-platform design.

The entirety of the Xamarin platform already provides a foundation of cross-platform design, but you can further the cause by understanding and thoughtfully implementing cross-platform architecture.

What is cross-platform architecture?

It begins with two kinds of code.

Shared Code and Platform-Specific Code

A Xamarin app can be broken down into the two types of code found in it:

  • Shared code: Used by all platforms in the app solution; also called cross-platform code.

  • Platform-specific code: Used by one OS platform in the app solution, such as iOS or Android, but not both.

There are a number of ways to divide an application solution between shared and platform-specific code. It can be done top-down by project, or bottom-up using individual lines of code; then there are middle ways using specific files or classes. Many good apps are divided into shared and platform-specific code at all these levels.

The options available for slicing and dicing an application top-down are Portable Class Libraries (PCL), projects compilable into a single DLL used by multiple platforms, and Shared Projects, which are recompiled in different platform contexts. Bottom-up options include conditional compilation, which is a platform-specific compilation demarcation around small blocks of code in a Shared Project. Conditional compilation is not available in a PCL, since the DLL is pre-compiled, so Dependency Injection (DI) can be used instead to create platform-specific classes against a common interface. Custom renderers, covered in Chapter 8, use DI to split out platform-specific UI classes in a Xamarin.Forms solution. File linking is used to share specific files between projects.

All of these techniques are used to solve the problem of platform-specific differences in a cross platform app, which is called divergence.

Handling Divergence

Divergence describes the need for platform-specific implementations in a cross-platform app because platform-specific differences cause their implementations to diverge from the main code in a solution. Examples include custom renderers in Xamarin.Forms apps where platform-specific UI diverges from the cross-platform approach, and when networking or push notifications implemented in a core library reach a point where they require local OS API access.

The following are the primary techniques for handling divergence in a cross-platform application at different levels of granularity.

  • At the project level, use platform-specific projects.

  • At the file level, use file linking, sometimes with partial classes or methods.

  • At the class level, use dependency injection in PCLs. In Shared Projects you have more options, including DI, partial classes, and conditional compilation.

  • At the method level, use partial methods.

  • At the code level, use conditional compilation for individual lines of platform-specific code.

Due to their encapsulated nature, PCLs can only use a couple of the preceding techniques without recompiling a new DLL for each platform: platform-specific projects and dependency injection. Recompiling a cross-platform PCL DLL for each platform implementation is not recommended. If you must recompile, consider using a Shared Project. Shared Projects can use all of the previously mentioned techniques as they are designed to be recompiled for each platform. I’ll say more about PCLs and Shared Projects shortly.

Tip

There is also divergence within single platforms due to variations between platform versions, such as Android API or iOS release versions, for example. Version divergence may include divergence between screen sizes or features in APIs that are added or deprecated.

These techniques lead to a few standard architectures for Xamarin cross-platform apps that you’ll look at now. With the acknowledgment that architecture overlaps but isn’t the same as solution structure, let’s begin with the Xamarin.Forms solution.

Xamarin.Forms Solution Architecture

Xamarin.Forms apps have a base solution pattern that was mentioned in Chapter 2. These are the main projects.

  • Xamarin.Forms: Cross-platform UI code in a PCL or Shared Project, which is called by one of the platform-specific projects.

  • Xamarin.Android: Android-specific code, including Android project startup.

  • Xamarin.iOS: iOS-specific code, including iOS project startup.

  • Windows Phone application: Windows Phone–specific code, including Windows Phone project startup.

  • Core Library: Shared code such as Business Logic Layer (BLL) and DAL using a PCL or Shared Project. (A core library may not be necessary in a small or prototype app since shared code can reside in the Xamarin.Forms project.)

All these projects can be created automatically by the project template except the core library, which must be added manually when needed. Figure 9-1 shows the projects in question.

Figure 9-1.
figure 1figure 1

Xamarin.Forms solution projects

Figure 9-2 shows another way of looking at the Xamarin.Forms solution that includes the app and architecture layer axes.

Figure 9-2.
figure 2figure 2

Xamarin.Forms solution architecture of iOS, Android, and Windows Phone apps

There are two types of shared code in a Xamarin.Forms solution: the shared UI code in the Xamarin.Forms project and the DAL and BLL in the Core Library project.

The platform-specific projects (Xamarin.Android, Xamarin.iOS, and Windows Phone) house startup code, custom renderers, and other platform-specific functionality such as services, notification, sensors, or networking. In a Xamarin.Forms solution, most of the UI is housed in the Xamarin.Forms project and only in platform-specific projects when there are custom renderers. Platform-specific projects handle divergence at the project level.

Note on Windows Phone Projects

Creating a Xamarin.Forms Solution on Windows will not create a Windows Phone project without having the Windows Phone SDK installed. Windows Phone apps cannot be developed using a Mac. Xamarin Studio does not support the creation of Windows Phone projects and these must be created in Visual Studio.

Note on iOS Projects  Creating a Xamarin.Forms Solution on a Windows machine will create an iOS project, but it will not be usable without a Mac build host.

Entirely platform-specific solutions keep most or all of the UI in platform-specific projects.

Platform-Specific Solution Architecture

Platform-specific apps have a base solution that looks similar to a Xamarin.Forms solution, minus the Xamarin.Forms project, and all the platform-specific projects contain a lot more code. Here are the projects in a typical platform-specific solution.

  • Xamarin.Android: Android-specific code.

  • Xamarin.iOS: iOS-specific code.

  • Windows Phone application: Windows Phone–specific code.

  • Core Library: Shared app logic such as BLL and DAL using a PCL or a Shared Project. (For a lighter-weight solution without a core library, you can use file linking to connect shared code that resides in a single platform’s project to each of the other platform’s projects.)

Figure 9-3 shows the Xamarin projects for a platform-specific solution.

Figure 9-3.
figure 3figure 3

Xamarin projects in a platform-specific solution

Figure 9-4 shows another way of looking at the platform-specific solution that includes the app and architecture layer axes.

Figure 9-4.
figure 4figure 4

Xamarin solution architecture for Xamarin.iOS, Xamarin.Android, and Windows Phone apps

The platform-specific projects here (all projects except Core Library) handle divergence at the project level. In many situations, these are created one at a time. For example, first the iOS version of an app is written, then the Android version. This is a useful way to build an app because the core library can be tested with a single platform and the UI can be worked out entirely on one platform before adding a second or third platform.

MVVM and MVC

Xamarin.Forms is strongly modeled after the MVVM pattern with built-in data binding (as discussed in Chapter 7). For MVVM with platform-specific apps, there are open source options such as MvvmCross and MVVM Light. Both MvvmCross and MVVM Light can be used with Xamarin.Forms. However, many features of MvvmCross overlap with Xamarin.Forms, providing diminishing returns on using them together.

Regarding MVC in platform-specific solutions, Xamarin apps are largely MVC-ish. You create data models by-hand (M in MVC), which are bound as data sources to fields and lists. XML layouts make up the View (the V in MVC), and Android Activities and iOS UIViewControllers can act as controllers; however, due to native OS UI architectures, the lines between the View and Controller can be somewhat blurry. Although iOS is touted as MVC-based, if you are accustomed to the strict separation of concerns (SOC) found in an ASP.NET MVC solution, you may find iOS’s idea of MVC to be diluted. Since storyboard-generated UIViews define mainly static aspects of the screen, the logic defining dynamic content is pushed down into the UIViewController(the Controller, or C, in MVC). Much of the storyboard-generated UIView is about as View-like as .aspx files were before the release of ASP.NET MVC, that is, not very View-like at all. There isn’t an equivalent of Razor in native mobile development (building HTML templates in Xamarin using Razor notwithstanding). The same is true of Android development. Layout XML files create largely static layouts and leave the job of populating the dynamic content into the page to the Activity, which can become enmeshed with business logic. Xamarin’s mission is to provide direct access to these APIs, not to change their fundamental pattern. So, for a lot of us, iOS and Android development will be a step back architecturally from stricter MVC.

Having this knowledge, however, is power. It is up to you to impose SOC in your own apps. You can use your UIViewControllers and Activities to hold primarily view-related logic, and separate out business logic into true controllers of your own construction. Don’t let the toolset hold you back. That’s what classes are for, after all. This is the advantage of getting to do all this in C#!

Core Library

The core library is a dedicated project in your solution where the DAL, BLL, and other non-UI platform-independent code can reside. Enterprises use the core library for professional-grade code separation, decoupling the presentation layer from the BLL and DAL, and to facilitate team development. The core library isn’t necessary for some prototype apps, small projects, or small teams. All of the content in the core library project could instead be placed in the Xamarin.Forms project to simplify the solution.

Tip

If you’re just starting out with Xamarin.Forms, consider putting your data access, business logic, and shared code in the Xamarin.Forms UI project, and hold off on using a core library for now. You can use folders inside the Xamarin.Forms project to organize your non-UI code (for example, /data, /utilities, etc.) If you’re just starting out with platform-specific apps, you can use file linking (explained later in this chapter) as a lightweight alternative to a core library.

In large-scale and/or enterprise-grade apps, non-UI shared code should go into a core library project, separate from the UI projects. Core libraries are typically implemented using PCLs or Shared Projects. Here’s what you might put in the core library:

  • DAL: Data access layer that may include SQLite access, data models, view models, repositories, cloud data access, and web services. See Chapter 7.

  • BLL: Business logic that cuts across and is independent of platforms.

  • Miscellaneous: Utilities, interfaces, cross-platform resources, and sundry necessities. The core library is a cross-platform catch-all for non-UI files, folders, and classes.

In a nutshell, put platform-independent, non-UI code in your core library.

It is natural for core library components to be moved into platform-specific projects if it becomes clear that they are not sharable. There are quite a few functions, such as certain types of local file access or OS services, which are platform-specific functions that must be placed in the platform-specific projects. If only a few lines of code in the core library need to be platform-specific, then conditional compilation can be employed in Shared Projects or dependency injection in PCLs. File linking is also a useful option in Shared Projects for creating partial classes with platform-specific files. More on these shortly.

Core libraries are sometimes created using shared libraries or, less commonly, using file linking in platform-specific apps instead of creating a dedicated core library project (usually by sharing BLL or DAL files from a platform-specific project to all the other platform-specific projects). An increasing number of core libraries and Xamarin.Forms projects use PCLs.

Portable Class Libraries (PCL)

PCLs are code projects that provide a built-in subset of the .NET Framework based on the selection of target platforms, such as Xamarin.Android and Xamarin.iOS (or .NET 4.5 or even Xbox!). PCLs can be compiled into a DLL once and then run on all target platforms, so they are ideal for cross-platform code sharing.

Shared code in a solution—such as a Xamarin.Forms project, business logic, or data access code—can be compiled into a PCL DLL for use with platform-specific projects or in other solutions altogether. Because of its decoupled nature, the PCL is particularly useful when it is distributed to other developers rather than used by a single developer.

Create a PCL in Visual Studio while on the Add New Project screen by selecting the Portable Class Library option. In Xamarin Studio, on the New Project screen, select the Portable Library option.

PCLs are configured at compile-time in Visual Studio or Xamarin Studio to target particular platforms using a profile. Profiles are configured to allow a PCL to run with Xamarin.iOS, Xamarin.Android, or Windows phone, as well as with other platforms. PCLs allow you to target these platforms:

  • Microsoft .NET Framework

  • Silverlight

  • Windows Phone

  • .NET for Windows Store apps

  • Xamarin.Android

  • Xamarin.iOS

  • Xbox

PCLs provide a convenient, decoupled component for holding shared code in a solution. The cost of using a PCL is that once the DLL is compiled, platform-specific customization requires a bit of extra work.

Important Note

In a PCL, you cannot add or link files nor use partial classes, partial methods, or conditional compilation for platform-specific implementations.

Because PCLs are constructed to avoid recompiling, platform-specific customization is usually done outside of the PCL in the platform-specific projects using dependency injection.

Dependency Injection

Dependency Injection (DI) is a design principle that helps developers include platform-specific functionality into an otherwise cross-platform class using Inversion of Control (IoC). IoC patterns are framework calls to specific implementations of general classes provided by the application. DI does this by passing the implementation into a constructor/setter.

DI is useful for platform-specific functions such as custom renderers, file handling, background services, and sensors. In your shared code (usually a PCL, but this will also work in a Shared Project or file), create an interface to define the methods and patterns to be implemented in each platform. Implement platform-specific subclasses of the base class in each respective platform-specific project. Then you can inject these platform-specific implementations into your shared code. DI handles divergence at the class level.

There are a few ways to implement the DI design principle, including interfaces, abstract classes, and inheritance. Built into Xamarin.Forms is a DI implementation called DependencyService, which implements DI using interfaces.

Note

DI is a way to implement the Gang of Four (GoF) Strategy and/or Bridge patterns. Microsoft added to this in Windows Presentation Foundation (WPF) and called it the Provider pattern. Xamarin.Forms implements a variation of the Provider pattern in DependencyService.

Using DependencyService

Xamarin.Forms provides a built-in DI implementation called DependencyService that allows you to create a base interface, and then build platform-specific implementation classes to be invoked in shared code. This involves three steps:

  1. 1.

    Interface: An interface in the shared code declares the class for platform-specific implementation.

  2. 2.

    Implementation: Platform-specific implementations of the interface are registered using [assembly] tags.

  3. 3.

    Invocation: The platform-specific code is invoked from the shared code using DependencyService.Get<InterfaceName>.MethodName.

Let’s look at an example that passes a simple string into a custom class, concatenates the name of the OS onto the beginning, and returns the string to the shared code caller.

I’ll begin with the interface.

Creating an Interface

Using DependencyService first requires an interface of the functionality you want to implement. Interfaces help to create a consistent architecture for specifying cross-platform feature sets with platform-specific implementations.

Create an interface for your cross-platform class, ICustomClass, with a GoNative method.

    public interface ICustomClass

    {

        string GoNative(string param);

    }

The second step is to create platform-specific implementations.

Important Note

Remember to provide an implementation of your interface in all platform-specific projects. The DependencyService.Get method requires this in order to resolve the reference; otherwise, a NullReferenceException error will be thrown at runtime.

Let’s begin with Android.

Android Implementation

android

In your Android project, create a platform-specific implementation of ICustomClass called CustomClass_Android.cs. This version of the CustomClass.GoNative returns the value "Android".

    class CustomClass_Android : ICustomClass

    {

        public CustomClass_Android() { }

        public string GoNative(string param)

        {

            return "Android " + param;

        }

    }

Register the class for use in DependencyService above the CustomClass_Android and namespace declarations.

    [assembly: Xamarin.Forms.Dependency(typeof(CustomClass_Android))]

Remember to reference the current project to resolve CustomClass_Android.

    using DependencyServiceExample.Android;

Now for the iOS implementation.

iOS Implementation

i OS

In the iOS project, create a platform-specific implementation of CustomClass called CustomClass_iOS.cs. This version of the CustomClass.GoNative returns the value "iOS".

    class CustomClass_iOS : ICustomClass

    {

        public CustomClass_iOS() { }

        public string GoNative(string param)

        {

            return "iOS " + param;

        }

    }

Register the class for use in DependencyService above the CustomClass_Android and namespace declarations.

    [assembly: Xamarin.Forms.Dependency(typeof(CustomClass_iOS))]

Remember to reference the current project to resolve CustomClass_iOS.

    using DependencyServiceExample.iOS; And next is the Windows Phone version.

And next is the Windows Phone version.

Windows Phone Implementation

windows phone

Lastly, in your Windows Phone project, create a platform-specific implementation of CustomClass called CustomClass_WindowsPhone.cs. This version of the CustomClass.GoNative returns the value "Windows Phone".

    class CustomClass_WindowsPhone : ICustomClass

    {

        public CustomClass_WindowsPhone () { }

        public string GoNative(string param)

        {

            return "Windows Phone " + param;

        }

    }

Register the class for use in DependencyService above the CustomClass_WindowsPhone and namespace declarations.

    [assembly: Xamarin.Forms.Dependency(typeof(CustomClass_WindowsPhone))]

Remember to reference the current project to resolve CustomClass_WindowsPhone.

    using DependencyServiceExample.WinPhone;

Now you’ll use these platform-specific implementations in the shared code.

Invocation of the Platform-Specific Class

Invoke the platform-specific implementation in your Shared Project or PCL.

    var text = DependencyService.Get<ICustomClass>()

       .GoNative("platform-specific implementation complete!");

Create a button on your main page and use this DI invocation in the button’s Clicked event.

    class MainPage : ContentPage

    {

        public MainPage()

        {

            var button = new Button

            {

                Text = "Go Native!",

                VerticalOptions = LayoutOptions.CenterAndExpand,

                HorizontalOptions = LayoutOptions.CenterAndExpand,

            };

            button.Clicked += (sender, e) =>

            {

                var text = DependencyService.Get<ICustomClass>()

                    .GoNative("platform-specific implementation complete!");

                DisplayAlert("GoNative Called", text, "OK");

            };

            Content = button;

        }

    }

Figure 9-5 shows the button waiting to call CustomClass.GoNative.

Figure 9-5.
figure 5figure 5

Button ready to call platform-specific implementations using DependencyService

When the button is clicked, DependencyService calls the platform-specific GoNative method of the ICustomClass implementation, passing in the text "platform-specific implementation complete!". The registered platform-specific version of CustomClass takes the text param, adds the name of the OS at the beginning of the passed-in text string, and returns it to the calling class in shared code. The calling class then displays the entire string in a DisplayAlert, as shown in Figure 9-6.

Figure 9-6.
figure 6figure 6

Platform-specific implementations of CustomClass

Tip

Other ways to implement Dependency Injection include abstract classes and inheritance.

CODE COMPLETE: Using DependencyService

Listings 9-1, 9-2, 9-3, 9-4, and 9-5 contain the complete code for this DependencyService example. Listing 9-1 contains the interface. Listing 9-2 contains the callout to the platform-specific implementation of CustomClass.GoNative using DependencyService. Listings 9-3, 9-4, and 9-5 contain the OS-specific implementations of ICustomClass.

This example can be found in the downloadable code in the DependencyServiceExample solution.

Listing 9-1. The Interface Resides in CustomClass.cs in the Xamarin.Forms Project

    public interface ICustomClass

    {

        string GoNative(string param);

    }

Listing 9-2. The DependencyService Callout Is in MainPage.cs in the Xamarin.Forms Project

    class MainPage : ContentPage

    {

        public MainPage()

        {

            var button = new Button

            {

                Text = "Go Native!",

                    VerticalOptions = LayoutOptions.CenterAndExpand,

                HorizontalOptions = LayoutOptions.CenterAndExpand,

            };

            button.Clicked += (sender, e) =>

            {

                var text = DependencyService.Get<ICustomClass>()

                    .GoNative("platform-specific implementation complete!");

                DisplayAlert("GoNative Called", text, "OK");

            };

            Content = button;

        }

    }

Listing 9-3. The Android Implementation Is in CustomClass_Android.cs in the Android Project

    using System;

    using DependencyServiceExample.Droid;

    [assembly: Xamarin.Forms.Dependency(typeof(CustomClass_Android))]

    namespace DependencyServiceExample.Droid

    {

        class CustomClass_Android : ICustomClass

        {

            public CustomClass_Android() { }

            public string GoNative(string param)

            {

                return "Android " + param;

            }

        }

    }

Listing 9-4. The iOS Implementation Is in CustomClass_iOS.cs in the iOS Project

    using System;

    using DependencyServiceExample.iOS;

    [assembly: Xamarin.Forms.Dependency(typeof(CustomClass_iOS))]

    namespace DependencyServiceExample.iOS

    {

        class CustomClass_iOS : ICustomClass

        {

            public CustomClass_iOS() { }

            public string GoNative(string param)

            {

                return "iOS " + param;

            }

        }

    }

Listing 9-5. The Windows Phone Implementation Is in CustomClass_WindowsPhone.cs in the Windows Phone Project

    using System;

    using DependencyServiceExample.WinPhone;

    [assembly: Xamarin.Forms.Dependency(typeof(CustomClass_WindowsPhone))]

    namespace DependencyServiceExample.WinPhone

    {

        class CustomClass_WindowsPhone : ICustomClass

        {

            public CustomClass_WindowsPhone() { }

            public string GoNative(string param)

            {

                return "Windows Phone " + param;

            }

        }

    }

There are third-party alternatives to using DependencyService and to coding your own DI implementation.

Third-Party and Open Source DI Containers

A number of useful third-party and open source DI containers exist to help you do Dependency Injection. Here are a few of them used by many developers:

  • TinyIoC : Simple open source IoC solution

  • Unity : Microsoft’s DI solution

  • AutoFac : Another useful open source IoC solution

Tip

Dependency Injection (DI) is a subset of the Inversion of Control (IoC) design principle.

When more flexibility is required in the shared code than a PCL affords, a Shared Project can be useful.

Shared Projects

Shared Projects contain shared code that can be recompiled into different applications (in the same solution or in different ones). Like the PCL, this is where a core library or Xamarin.Forms project can be housed for use in different platform contexts. The Shared Project is particularly useful when it is used by a single developer, as it produces only a shareable code project, not a DLL.

Create a Shared Project in Visual Studio by navigating to File ➤ New Solution and choosing a name for the project and solution. In Xamarin Studio, navigate to File ➤ New Solution and choose a name.

Tip

The Shared Project solution template requires Visual Studio 2013 Update 2 (or Xamarin Studio).

There are many techniques available for handling divergence in Shared Projects: Dependency Injection (DI), conditional compilation, file linking, partial classes, and partial methods. I’ve already discussed DI, so let’s look at the rest of these common approaches:

  • Conditional compilation: Compiler directives for small, code-level amounts of divergence

  • File linking: Project file include for file or class-sized levels of divergence

  • Partial classes: Using the partial keyword for class divergence

  • Partial method: Using the partial keyword for method divergence

These approaches can be used loosely, as needed, without much structure. Use them carefully, because implementations of any of them can become an anti-pattern, tightly linking cross-platform and platform-specific classes without proper organization.

An architecturally disciplined approach might instead use the Strategy pattern with the Bridge pattern. The Strategy pattern encapsulates the various platform behaviors in a class to abstract away the platform-specific implementations. The Bridge pattern is used to decouple the class or method declarations in the shared code from the platform-specific implementations using an interface, abstract classes, or inheritance. You can also use DI, as discussed earlier.

Let’s look at each of the techniques in turn, starting with conditional compilation. The Shared Project is an ideal place to put code that requires slight variations between platforms using conditional compilation.

Conditional Compilation

When shared code requires slight platform-specific variations (that cannot be accounted for using the Xamarin.Forms OnDevice command), you can create compiler directives that compile code conditionally by platform. This is a useful way to inject small pieces of platform code into an otherwise shared block of code. This is a way to handle divergence at the smallest granularity: line-by-line.

Xamarin solution templates predefine three compilation symbols: __IOS__, __ANDROID__, and __MOBILE__ (two underscores before and after each term). Use these compilation symbols with the #if/#endif compiler directives to include and exclude code based on the project platform.

Specify an iOS-specific block of code using #if __IOS__.

    #if __IOS__

    // iOS-specific code

    #endif

Specify Android using #if __ANDROID__.

    #if __ANDROID__

    // Android-specific code

    #endif

Specify either iOS or Android using #if __MOBILE__.

    #if __MOBILE__

    // iOS or Android-specific code

    #endif

Android API level can also be specified in the conditional. This is a way to manage platform-version divergence.

    #if __ANDROID_22__

    // code for Android API 22 or newer

    #endif

Conditional compilation is useful for platform-specific exceptions in a cross-platform class. If there are too many exceptions, you should divide your features into platform-specific implementations and put them into their respective projects, that is, in the Xamarin.iOS project. You can do this in an architecturally sound manner by using interfaces to define the feature set before creating platform-specific implementations using the Strategy and/or Bridge pattern (or DI).

File Linking

You can bring individual files into projects from other projects using file linking. This can be used to bring platform-specific implementations into Shared Projects or shared code into platform-specific projects.

Simply right-click a project and choose Add (in Xamarin Studio) or Add Files (in Visual Studio). Pick the desired file or folder and choose Link when prompted to virtually include it in your current project.

A common use of file linking is for small or prototype platform-specific apps where a lightweight alternative to a core library is needed. You can put all of your shared files (business logic, data access, utilities, and so on) into a single project (such as the Android or Xamarin.Forms projects), and then link to those files from the other platform-specific projects (such as the iOS and/or Windows Phone projects). File linking instead of a core library is a quick and dirty approach that probably shouldn’t be used on enterprise-grade or team projects.

Tip

Overuse of bottom-up, fine-grained approaches such as file linking and conditional compilation can lead to architecturally undisciplined code, so consider your top-down alternatives first (like PCLs and Shared Projects).

Partial Classes and Methods

Partial classes are useful for extending a shared, cross-platform class with platform-specific functionality. Partial methods are useful for implementing platform-specific functionality at the method level, though without a base cross-platform implementation. These techniques work in Shared Projects and linked files, but not PCLs.

Create a partial class in your Shared Project.

    public partial class Utility

    {

        public void DoCrossPlatformThing()

        {

        }

    }

Then extend that class in your platform-specific project.

    public partial class Utility

    {

        public void DoPlatformSpecificThing()

        {

        }

     }

Partial methods are similar to partial classes, although they don’t provide a cross-platform implementation, only a platform-specific one.

Define a partial method in your Shared Project, but do not provide an implementation.

    partial void DoThing();

Then implement that method in your platform-specific projects.

    partial void DoThing()

    {

    }

Partial classes are a common method for architecting for divergence in Shared Projects and linked files. Divergence isn’t just for platforms, though; it happens with OS versions as well.

Handling Version Divergence

As new releases of mobile OSs take place and new devices are released, changes such as screen size and feature deprecation must be accounted for. Old features must be gracefully deprecated and new features are eagerly embraced. All this can happen on a single platform, such as iOS or Android, creating a process called version divergence.

In order to account for this type of divergence, your apps must be aware of the active OS version in certain situations and respond accordingly. This requires version detection. Here are a few ways to do that.

On Android, you can use the following:

    if (((int)Android.OS.Build.Version.SdkInt) >= 22)      

    {

      // code for Android API 22 or newer

    }

And when using conditional compilation, there is an option on Android.

    #if __ANDROID_22__

      // code for Android API 22 or newer

    #endif

On iOS, you use CheckSystemVersion.

    if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))

    {

      // code for iOS version 8.0 or newer

    }

Those are some techniques for handling OS version divergence. Remember to gracefully degrade deprecated functions and provide a lowest-common denominator functionality when necessary.

Summary

After you’ve decided whether you’re going with Xamarin.Forms or a platform-specific approach, you need to craft an architecture. Split your cross-platform apps into two types of code: shared and platform-specific. Divide your app into these two groups starting with the largest divisions and working down toward the smaller ones.

A core library is useful in a professional-grade Xamarin app for holding your BLL, DAL, utilities, interfaces, and other back-end, non-UI, cross-platform classes.

Platform-specific code should go into its respective platform-specific projects—into a Xamarin.Android project, for example. Custom renderers in Xamarin.Forms projects are placed in these platform-specific projects.

Cross-platform projects include Xamarin.Forms projects and core libraries. These are typically built using Portable Class Libraries (PCL) or Shared Projects. PCLs result in pre-compiled cross-platform DLLs, which are ideal for sharing between developers. Shared Projects produce shared code projects designed to be recompiled for each platform and are generally more useful for a single developer.

Plenty of cross-platform classes call for platform-specific code and the decision must be made as to how to handle these exceptions. Granularities that are smaller than an entire project require special treatment in the handling of shared versus platform-specific code. Useful design principles include Dependency Injection (DI) and Inversion of Control (IoC). Useful design patterns include the Provider, Strategy, and Bridge patterns.

When using cross-platform PCLs, Dependency Injection (DI) is the platform-specific weapon of choice, and Xamarin.Forms provides a handy DependencyService class for easy DI implementation. Because Shared Projects are more tightly coupled to platform-specific projects than PCLs and must be recompiled per platform, they afford more options, including DI, conditional compilation, partial classes and methods, and file linking. These techniques can be used to create loosely-constructed explicit implementations of platform-specific tasks, so be on the lookout for anti-patterns when using Shared Projects.

Those are the fundamental principles, patterns, and divergence-handling techniques in Xamarin mobile app architecture. I hope you find everything you need here to go forth and build!