Calling native platform code in your portable class library (PCL)
is achievable via Dependency Injection.
It’s a common question for people starting out, who are using a
PCL or .NET Standard Library for developing their Xamarin apps. Dependency
Injection involves creating an interface that can be commonly applied across
all native platforms, then coding the actual implementation, or the native
platform code, on each platform.
This interface and instance is then given to a container. The PCL
or .NET Standard Library, then goes to the container with only the interface
known, to retrieve the instance. It doesn’t know where the instance came from,
just that it has a specific interface.
“Wikipedia
definition “ - In software engineering, dependency
injection is a technique whereby one object (or static method)
supplies the dependencies of another object. A dependency is an object that can
be used (a service).
Before understanding what it means in programming, let’s first see
what it means in general as it will help us understand the concept better.
Dependency or dependent means relying on something for support.
Like if, I say we are relying too much on mobile phones than it means we are
dependent on them.
So before getting to dependency injections, first let’s understand what
a dependency in programming means.
When class A uses some functionality of class B, then its said
that class A has a dependency of class B.
In C#, before we can use methods of other classes, we first need
to create the object of that class (i.e. class A needs to create an instance of
class B).
So,
transferring the task of creating the object to someone else and directly using
the dependency is called dependency injection.
Why should I use dependency injection?
Let’s say we have a car class, which contains various objects such
as wheels, engine, etc.
private Wheels
wheel = new MRFWheels();
|
|
private Battery
battery = new ExcideBattery();
|
|
}
Here the car class is responsible for creating all the dependency
objects. Now, what if we decide to ditch MRFWheels in
the future and want to use Yokohama
Wheels?
We will need to recreate the car object with a new Yokohama
dependency. But when using dependency injection (DI), we can change the Wheels
at runtime (because dependencies can be injected at runtime rather than at
compile time).
You can think of DI as the middleman in our code who does all the
work of creating the preferred wheels object and providing it to the Car class.
It makes our Car class independent from creating the objects of
Wheels, Battery, etc.
There are
basically three types of dependency injection:
- constructor injection: the
dependencies are provided through a class constructor.
- setter injection: the
client exposes a setter method that the injector uses to inject the
dependency.
- interface injection: the
dependency provides an injector method that will inject the dependency
into any client passed to it. Clients must implement an interface that
exposes a setter method that accepts the dependency.we will learn this in
detail below using example of Xamarin forms applications.
/*Somewhere in our codebase we
instatiate the objects required by this class.
|
|
There are two methods for
implementing DI:
|
|
Car(Wheel wh, Battery bt) {
|
|
}
So now its the dependency injection’s responsibility to:
- Create
the objects
- Know
which classes require those objects
- And
provide them all those objects
If there is any change in objects, then DI looks into it and it should
not concern the class using those objects. This way if the objects change in
the future, then its DI’s responsibility to provide the appropriate objects to
the class.
Interface
The
first thing we must do to enable dependency injection is define an interface.
For this example, lets say we have to get a unique identifier for the mobile
phone. There are specific calls you can make on a each platform to get this,
but its not available in our PCL. Hence we would define this interface to
return a string of unique identifier.
public interface IDeviceInfo
{
string GetUniqueIdentifier();
}
This
interface would be placed in your PCL and hence accessible from your PCL and
each platform project.
Implementation
Next
we need to actually define the implementation of this interface in each
platform.
iOS
public class DeviceInfo : IDeviceInfo
{
public string GetUniqueIdentifier()
{
return UIDevice.CurrentDevice.IdentifierForVendor.AsString();
}
}
Android
public class DeviceInfo : IDeviceInfo
{
public string GetUniqueIdentifier()
{
return Android.Provider.Settings.Secure.GetString(Xamarin.Forms.Forms.Context.ContentResolver,
Android.Provider.Settings.Secure.AndroidId);
}
}
UWP
public class DeviceInfo: IDeviceInfo
{
public string GetUniqueIdentifier()
{
var token = HardwareIdentification.GetPackageSpecificToken(null);
var hardwareId = token.Id;
var dataReader = Windows.Storage.Streams.DataReader.FromBuffer(hardwareId);
byte[] bytes = new byte[hardwareId.Length];
dataReader.ReadBytes(bytes);
return BitConverter.ToString(bytes);
}
}
Dependency Injection Container Registration
Next
we want to register this implementation with the Dependency Injection
Framework. For this example, I will just use Xamarin Forms inbuilt dependency
injection container. Place the following above each implementation of the
DeviceInfo, above the namespace declaration.
[assembly: Xamarin.Forms.Dependency(typeof(DeviceInfo))]
namespace Mobile.Platform
{
...
You
can also register it via a line of code, and this is actually need for UWP, as
the assembly registration has issues with UWP under Native Compilation.
Xamarin.Forms.DependencyService.Register<DeviceInfo>();
Retrieve Dependency
Lets
now retrieve that dependency in code. All we need to do is call this line,
referencing the interface we want the implementation of.
DependencyService.Get<IDeviceInfo>().GetUniqueIdentifier();
Now
we can call the GetUniqueIdentifier and it will return a string. The PCL or
.NET Standard Library doesn’t know anything about the platform, and just
interact with the instance via the interface definition.
AutoFac
The
Xamarin Forms Dependency Service is a nice simple dependency injection
framework, but it lacks many features, including the ability to do constructor
injection. Using something more advanced such as AutoFac offers you many more
features. There are many other Dependency Injection Frameworks out there, and
they all provide very similar functionality.
To
create the container, implement the following code.
ContainerBuilder _builder = new ContainerBuilder();
Now
you can add dependencies as needed.
_builder.RegisterInstance<IDeviceInfo>(new DeviceInfo()).SingleInstance();
Finally
you need to call the Build function to finish the building of the container.
_builder.Build();
The
Container should be accessible in the PCL and native platforms, as is the
interface. Similar to Xamarin Forms implementation, you can also get the
Constructor Injection
The
benefits of using a Dependency Injection Framework such as AutoFac, is you can
inject dependencies into a constructor. For example.
public class MyViewModel
{
private readonly IDeviceInfo _deviceInfo;
public MyViewModel(IDeviceInfo deviceInfo)
{
_deviceInfo = deviceInfo;
}
}
If
you had registered your ViewModel and DeviceInfo, in AutoFac, when AutoFac
creates an instance of your ViewModel, it will automatically detect
constructors with parameters and try to fill them in as needed. This has
immense benefits for readability and unit testing. No longer is a dependency in
your class, hidden via a line of code, its clearly apparent, as you can only
create the class, if you inject the dependency into it.
Other Frameworks
You
might be wondering what other frameworks are out there. First they are some
MVVM frameworks that come with one included such as FreshMVVM and MVVMLight. If you prefer to keep your MVVM
frameworks and Dependency Injection Framework separate, you can also use Ninject, TinyIoc, Unity, Castle Project, StructureMap and Spring.NET, just to name a few. You
can do some research to find the one that best meets your needs. Personally, I
prefer AutoFac, as its recent
speed improvements, along with powerful interface, features and maturity make
it great to work with.
Inversion of
control —the concept behind DI
This states that a class should not configure its dependencies
statically but should be configured by some other class from outside.
Benefits of
using DI
- Helps in Unit testing.
- Boiler plate code is reduced, as initializing of dependencies
is done by the injector component.
- Extending the application becomes easier.
- Helps to enable loose coupling, which is important in
application programming.
Disadvantages
of DI
- It’s a bit complex to learn, and if overused can lead to
management issues and other problems.
- Many compile time errors are pushed to run-time.
- Dependency injection frameworks are implemented with
reflection or dynamic programming. This can hinder use of IDE automation,
such as “find references”, “show call hierarchy” and safe refactoring.
You can implement dependency injection on your own (Pure Vanilla)
or use third-party libraries or frameworks.