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.
1
2
3
4
5
6
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
1
2
3
4
5
6
7
8
9
10
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
1
2
3
4
5
6
7
8
9
10
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
1
2
3
4
5
6
7
8
9
10
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
1
2
3
4
5
6
7
8
9
10
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
1
2
3
4
5
6
7
8
9
10
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
1
2
3
4
5
6
7
8
9
10
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