devxlogo

Cross-Platform Mobile Development with Xamarin

Cross-Platform Mobile Development with Xamarin

In Text Rendering with Xamarin, I drilled down into the countless ways and options that Xamarin provides you to display text on the screen of multiple mobile devices. In this article, I’ll focus on the cross-platform mechanisms and the support for per-platform code in Xamarin.

SAP vs. PCL

Xamarin Forms provides two different paths to cross-platform applications. The shared assets project (SAP) contains files that are included directly with each platform’s project at build time. The portable class library (PCL) is different and creates a share dynamic link library that is loaded at runtime by each platform’s application. The difference is important, as you’ll soon see, because there are different techniques you can use when you know at build time what platform the code is going to run on vs. at runtime only.

Code vs. XAML

Xamarin Forms exposes a cross-platform object model for laying out UI elements on the screen, but also a declarative XML-based language called XAML (Extensible Application Markup) to describe the visual tree. XAML is surprisingly expressive and while code is almost always needed for event handler you can define pretty much anything in XAML. That includes conditionally specifying XAML based on the target platform.

Conditional Pre-processor Code (SAP Only)

Xamarin supports five different platforms: iOS, Android, Universal Windows platform, Windows 8.1 and Windows Phone 8.1. When you use a SAP project and build for different platforms Xamarin defines a special symbol for the target platform. Then, in the code you can execute platform-specific code wrapped by special pre-processor macros that test for that symbol. Here is how:

#if __IOS__// iOS specific code#elif __ANDROID__// Android specific code#elif WINDOWS_UWP// Universal Windows Platform specific code#elif WINDOWS_APP// Windows 8.1 specific code#elif WINDOWS__PHONE_APP// Windows Phone 8.1 specific code#endif

In iOS, if you place elements at the top of the page, they’ll show under the status bar. To address that you can add some padding for iOS programs. But, the padding is not needed for the other platforms. The conditional pre-processing is perfect for that:

#if __IOS__ Padding = new Thickness(0, 20, 0, 0); #endif 

The Device Class

That solution works well for SAP projects, but there is another solution that works for either SAP or PCL. The Device class provides several static members that can be used at runtime to determine what platform the code is running on. Device.OS returns the TargetPlatform enumeration with one of the values: iOS, Android, WinPhone or Other. The Device.Idiom returns the TargetIdiom enumeration with one of the values: Phone, Tablet, Desktop or Unsupported.

You can set the padding dynamically for iOS using:

if Device.OS == TargetPlatform.iOS{Padding = new Thickness(0, 20, 0, 0);}

Device.OnPlatform in Code

But, there is an even better solution. The Device class has a generic static method called OnPlatform that takes 3 arguments and returns one of the depending on the platform. The first argument stands for iOS the second for Android and the third for all Windows platforms. If you need to distinguish between Windows platforms you’ll have to use Device.OS. So, to add padding just for iOS using Device.OnPlatform you can use the following code:

Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0); 

No conditional code is needed.

OnPlatform in XAML

All the techniques so far required writing code, but one of the best properties of Xamarin is that you can specify most, if not all, of the visual tree using the declarative XAML. Is it possible to write declarative cross-platform XAML that takes into account differences between platforms? It turns out is possible!

The OnPlatform class is designed especially for this use case:

public class OnPlatform { public T iOS { get; set; } public T Android { get; set; } public T WinPhone { get; set; } public static implicit operator T(OnPlatform onPlatform) { // returns one of the three properties based on Device.OS } }

You can use it in XAML like so together with the x:TypedArguments attribute to specify the type T:

   ...

Dependency Service

These techniques work well for small snippets or values, but what if you really need to provide significant amounts of custom code for different platforms? In this case, it is much better to encapsulate it in separate classes that expose the same interface and then in each platform specific project implement a different class.

Xamarin provides the dependency service mechanism to help with this task. It includes an assembly attribute to decorate the class and a static method to get the custom instance. For example, let’s define an interface:

public interface ICustomPlatform{void DoWork();}

Each platform will define a class called CustomPlatformInfo that implements this interface and will be marked by the assembly attribute as a Dependency:

For IOS

 [assembly: Dependency(typeof(CustomPlatform.iOS.ICustomPlatform))] 

For Android:

 [assembly: Dependency(typeof(CustomPlatform.Droid.ICustomPlatform))] 

To use it from a common code you get the interface from the DependencyService class that knows how to scan the dependent assemblies and using reflection get an instance of the requested interface:

IPlatformInfo platformInfo = DependencyService.Get();

Conclusion

Xamarin Forms is designed from the get-go to provide a cross-platform experience, but it recognizes that sometimes you have to work with platform-specific code or features. To address that need, Xamarin provides multiple mechanisms for various use cases and workflows.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist