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.
class Car{
|
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.
class Car
{
|
private Wheels wheel;
|
private Battery battery;
|
/*Somewhere in our codebase we
instatiate the objects required by this class.
|
There are two methods for
implementing DI:
|
1. Constructor based
|
2. Setter based
|
*/
|
// Constructor Based
|
Car(Wheel wh, Battery bt) {
|
this.wh = wh;
|
this.bt = bt;
|
}
|
// Setter Based
|
void setWheel(Wheel wh){
|
this.wh = wh;
|
}
|
...
|
...
|
// Rest of code
|
}
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 theConstructor 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.
No comments:
Post a Comment