How To Use Abstractions To Implement Platform Specific Functionality In Xamarin.Forms

In this article, we will learn how to use abstractions to implement platform specific functionality in our Xamarin forms shared code. Therefore, Xamarin forms have some useful APIs but they do not expose everything from the platform. In fact, it's very likely that there will be some feature, which you need to use, which is not available to your shared code. For these cases, we have to put a little architectural thought into our Application. We want to use the APIs that are available in the platform specific project but somehow invoke them from our shared code.

xamarin

As an example, consider dialing the phone. All three platforms have support for this feature but each accomplishes it differently. To fully control it, we would want to write the code uniquely per platform. Let’s explore what Xamarin forms has to offer here. The platform features do not expose those Xamarin forms, which can be used but will require some architectural design.

xamarin

First, you want to create some form of abstractions, which might be an interface or base class, which is something to represent the feature that you want to use in your shared code. The best practice is to build an abstraction implemented by the target platform, which defines the platform specific functionality. Here, we define an IDialer interface, which describes how our code will dial the phone on each platform. In each platform specific projects, we'll implement that the interface uses the platform specific API, which is available to us in those projects. Our shared code will always use the interface, which means it's not tied directly to the implementation or platform-specific code projects implement the shared Dialer interface, using the platform specific API to locate the implementation. We can use a variety of techniques often. It's either a variation of the Service locator design pattern or Dependency Injection design pattern. It really does not matter how you bind to the implementation; the key thing is the separation being used here when creating your own abstraction. You can of course introduce your own locator object or provide a singleton property, which your platform-specific code assigns.

Alternatively, Xamarin forms has two built-in mechanisms.

xamarin

The first is a generalized messaging Service called Messaging Center, which has a publish/subscribe event system. You can send the messages from anywhere in your shared code and have some handler likely in each of the platform libraries to receive the message and then process it in the platform specific way. This is exactly what page does when creating the alerts and the action sheets.

The second way is to use a Dependency Service API. This is specifically designed to locate and bind the interfaces or the abstract classes to the implementations.
xamarin

When using the dependency Service, the first step is to define our abstraction. The IDialer interface in this case must be in our shared code since all the parties need to know about this class or an interface.
xamarin

Next, we will implement the abstraction in each of our platform specific projects. Here, we define it for an iOS but we would also want to do an implementation in an Android and UWP projects. Notice that it does not need to be public; the external users won't know about the class directly. They will only reference it by the abstraction.
xamarin

In each of the platforms, specific projects will let the Dependency Service know about our implementation, using the dependency attribute. This is declared at the assembly level, as you can see here and identifies the implementation, which is then registered with the Dependency Service Xamarin forms. You can also use a new register method to add specific instances or types to the Dependency Service. There is a feature, which allows you to make runtime decisions about the implementation of the Service or to instantiate a Service implementation with the parameterised constructor.
xamarin

Finally, anywhere in your app; you can request the abstraction from the Dependency Service. It will return the first registered class, which implements the specific interface or derives from the specified base class. If no classes are found, null is returned; which means that you can implement a Service for one platform but leave it off for another; if it's not possible to implement or it's optional.