I'm Andrew Hoefling, and I work for FileOnQ as a Lead Software Engineer building mobile technologies for Government, Financial and First Responders using Xamarin. 

 

Platform Specific C# in Uno Platform


The Uno Platform Getting Started blog series is a blog series that I put together for October of 2020. It contains several articles that will help you get started building scalable enterprise applications in Uno Platform. Be sure to checkout all the blogs in the series by heading to the 1st article - Uno Platform Getting Started Series

When building cross-platform apps using Uno Platform it is a common problem to run platform specific code. You may have the same User Interface action but the code needs to execute native APIs or just respond differently depending on the different business rules. Uno Platform provides easy to use pre-processor directives to make this easy to use.

Uno Platform has amazing documentation on how to use the various features and customization. Take a look at the official documentation for the latest and greatest information regarding platform specific directives.

Pre-processor Directives in C#

In C# the concept of pre-processor directives is a fancy word for #if statements that allow you to run code in specific scenarios. The most common pre-processor directive in C# is the DEBUG symbol. You may have seen this already in C# for printing debug statements like in the code sample below.

public void MyMethod()
{
#if DEBUG
    Debug.WriteLine("This is a debug statement");
#endif
}

In any C# project you can customize the pre-processor directives or symbols with anything that you want to use #if statements for. Fortunately for us, Uno Platform has this already built into the platform so you don't need to worry about configuring your .csproj file. They just work out of the box.

Uno Platform Symbols

The symbols are already configured for you out of the box in Uno Platform, all you need to do is start using them when you want to run platform specific code.

  • UWP - NETFX_CORE
  • Android - __Android__
  • iOS - __IOS__
  • Web Assembly - __WASM__
  • MacOS - __MACOS__

Suppose you have a method that returns a Hello World message, you can use the above mentioned symbols to display customized messages for each platform.

public string HelloWorld()
{
    string message = string.Empty;

#if NETFX_CORE
    message = "Hello from UWP World!";
#elif __ANDROID__
    message = "Hello from Android World!";
#elif __IOS__
    message = "Hello from iOS World!";
#elif __WASM__
    message = "Hello from Web Assembly World!";
#elif __MACOS__
    message = "Hello from MacOS World!";
#endif

    return message;
}

Advanced Scenarios

The .csproj specification allows the project to build specific files for pre-defined platforms. this technique is very common in cross-platform SDKs used throughout the Xamarin and Uno Platform community. This same idea can be applied to your Uno Platform app, where you create a partial class for each platform. This reduces the verbosity of all the #if statements that will be included in your code.

Partial Classes

Out of the box in Uno Platform the easiest way to accomplish this is to create your platform specific code in the specific project heads, where the namespace will make the shared API. Consider the example above where we want to return a string from a method. Let's create a simple MessageService that has a method called GetData()

Shared MessageService.cs

namespace PlatformSpecificSample.Services
{
    public partial class MessageService
    {
        public string RetrieveMessage()
        {
            return this.GetNativeData();
        }
    }
}

Before we start adding our platform specific implemntations, lets digest the shared code. Currently this will not build as we don't have any platforms implemented. The 2 most important parts of this code snippet are

  • The Partial Class - Partial classes allow you to define the same class across multiple files and at compile time it creates 1 complete class. 
  • The Namespace - When defining partial classes each partial class must share the exact same namespace.

In your UWP project create the following implementation

UWP MessageService.cs

namespace PlatformSpecificSample.Services
{
    public partial class MessageService
    {
        public string GetNativeData()
        {
            return "Hello from UWP World!";
        }
    }
}

In your Android project create the following implementation

Android MessageService.cs

namespace PlatformSpecificSample.Services
{
    public partial class MessageService
    {
        public string GetNativeData()
        {
            return "Hello from Android World!";
        }
    }
}

In your iOS project create the following implementation

iOS MessageService.cs

namespace PlatformSpecificSample.Services
{
    public partial class MessageService
    {
        public string GetNativeData()
        {
            return "Hello from iOS World!";
        }
    }
}

In your Web Assembly project create the following implementation

Web Assembly MessageService.cs

namespace PlatformSpecificSample.Services
{
    public partial class MessageService
    {
        public string GetNativeData()
        {
            return "Hello from Web Assembly World!";
        }
    }
}

In your MacOS project create the following implementation

MacOS MessageService.cs

namespace PlatformSpecificSample.Services
{
    public partial class MessageService
    {
        public string GetNativeData()
        {
            return "Hello from MacOS World!";
        }
    }
}

Partial classes that are added in the native platform specific project heads remove the need for using the #if statements. This greatly simplifies your code, but introduces a new organization problem of where to store all of the partial classes. Fortunately using tools like Visual Studio and other IDEs make it easy to navigate and find the different classes.

Conclusion

When building cross-platform apps there is always a need to execute platform specific code. Uno Platform provides all the tools you need for basic and advanced scenarios to execute your code in these different scenarios.

-Happy Coding


Share

Tags

.NETC#iOSUWPAndroidWeb AssemblyWASMMacOS