Device.OnPlatform<T>(Default)

Xamarin Forms is an amazing framework despite a lot of bugs.

It gets better each time it is updated and I am pretty confident it will be mature enough to build any app in a close future, providing you have the time and experience to do so.

But as all framework under development it has its flaws.
Sometimes it is big flaws. Usually, people notice big flaws and developpers fix them for the best. Ironically it is not those flaws that bugs me.

Because sometimes it is these minor things that annoy the end-user developper but that go unnoticed just because it is not a problem enough to need a quick fix.


Device.OnPlatform

And a good example in my opinion is these methods to define platform-specific code:

OnPlatform<T>(T iOS, T Android, T WinPhone)  
OnPlatform(Action iOS = null, Action Android = null, Action WinPhone = null, Action Default = null)  

It seems cool right ? It is. It allows us to define values and snippets of code for each platform like this :

FontSize = Device.OnPlatform<double>(10, 10, 30);  

It is an incredible tool. But one thing bothers me.

Here I have defined a size of 10 for both Android and iOS. It is okay because I do not mind writing twice « 10 ». But when it comes to more complex objects with parameters and all I hate to define it twice / outside. I find it awful for maintainability.

So I end up using something like that (once again I took a very simple example but I only do this for complex situations) :

Device.OnPlatform(  
    WinPhone: () => FontSize = 30, 
    Default: () => FontSize = 10);

Which is terribly unsatisfying as I have to use the Action-based method, the only one that support this Default mechanism.


Implementation of generic Default mechanism

Why not define a Default mechanism for the typed-version ? Probably because it is a bit trickier as a generic method cannot use null as default value (because not every object can take the null value, ex: bool).
But I need it so let's figure this out.

There are two workarounds : use default(T) or restrict T to classes (where T : class).
I chose to use default(T) because I want to extend this to any type.

So here is my final implementation of Device.OnPlatform<T>(Default) :)

public static T OnPlatform<T>(T iOS = default(T),  
                                T Android = default(T), 
                                T WinPhone = default(T), 
                                T Default = default(T))
{
    return Device.OnPlatform<T>(
        object.Equals(iOS, default(T)) ? Default : iOS, 
        object.Equals(Android, default(T)) ? Default : Android, 
        object.Equals(WinPhone, default(T)) ? Default : WinPhone);
}

And the usage :

FontSize = DeviceExtensions.OnPlatform<double>(WinPhone: 30, Default: 10);  

Which solves my initial problem. And I hope yours too.
Enjoy :)

Paul, out.