Using the DependencyService with DEM
Xamarin.Forms includes a DependencyService to let shared code to easily resolve Interfaces to platform-specific implementations, allowing you to access features of the iOS and Android from your PCL or Shared Project.
The problem with Xamarin's DependencyService is that it requires a static call to DependencyService.Get<> in your shared code to get a platform-specific instance of the interface at run time. This makes your Services or other elements less testable, and hides the dependencies of your class.
DEM allowing you to simply request any dependencies that have been registered with AutoFac via your class constructor.
Step 1: Add an interface to the shared project
Add an interface to the shared project that will define the contract for our service:
public interface IUniqueDeviceIdentifierFactory : IApplicationService
{
string GetId();
}
Step 2: Implement our interface
Add a class that implements our interface to each specific platform
iOS
public class OSUniqueDeviceIdentifierFactory : IUniqueDeviceIdentifierFactory
{
string id = null;
public string GetId()
{
return id ?? (id = GetSerialNumber());
}
[DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
static extern uint IOServiceGetMatchingService(uint masterPort, IntPtr matching);
[DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
static extern IntPtr IOServiceMatching(string s);
[DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
static extern IntPtr IORegistryEntryCreateCFProperty(uint entry, IntPtr key, IntPtr allocator, uint options);
[DllImport("/System/Library/Frameworks/IOKit.framework/IOKit")]
static extern int IOObjectRelease(uint o);
string GetSerialNumber()
{
var serial = string.Empty;
try
{
var platformExpert = IOServiceGetMatchingService(0, IOServiceMatching("IOPlatformExpertDevice"));
if (platformExpert != 0)
{
var key = (NSString)"IOPlatformSerialNumber";
var serialNumber = IORegistryEntryCreateCFProperty(platformExpert, key.Handle, IntPtr.Zero, 0);
if (serialNumber != IntPtr.Zero)
{
serial = Runtime.GetNSObject<NSString>(serialNumber);
}
IOObjectRelease(platformExpert);
}
}
catch (Exception)
{
}
return serial;
}
}
Andoroid
public class DroidUniqueDeviceIdentifierFactory : IUniqueDeviceIdentifierFactory
{
static JniPeerMembers buildMembers = new XAPeerMembers("android/os/Build", typeof(Build));
static string GetSerialField()
{
try
{
const string id = "SERIAL.Ljava/lang/String;";
var value = buildMembers.StaticFields.GetObjectValue(id);
return JNIEnv.GetString(value.Handle, JniHandleOwnership.TransferLocalRef);
}
catch
{
return string.Empty;
}
}
string id = string.Empty;
public string GetId()
{
if (!string.IsNullOrWhiteSpace(id))
{
return id;
}
id = GetSerialField();
if (string.IsNullOrWhiteSpace(id) || id == Build.Unknown || id == "0")
{
try
{
var context = CrossCurrentActivity.Current.Activity ?? Application.Context;
id = Secure.GetString(context.ContentResolver, Secure.AndroidId);
}
catch (Exception ex)
{
//Android.Util.Log.Warn("DeviceInfo", "Unable to get id: " + ex.ToString());
}
}
return id;
}
}
Step 3: Register with IPlatformInitializer
iOS
public class OsIntializer : IPlatformInitializer
{
public virtual void RegisterTypes(ITypeCatalog typeCatalog)
{
typeCatalog.Add(new AppServiceInfo<DroidUniqueDeviceIdentifierFactory, IUniqueDeviceIdentifierFactory>());
}
}
Andoroid
public class DroidIntializer : IPlatformInitializer
{
public void RegisterTypes(ITypeCatalog typeCatalog)
{
typeCatalog.Add(new AppServiceInfo<OSUniqueDeviceIdentifierFactory, IUniqueDeviceIdentifierFactory>());
}
}
Step 4: Use the Service
public DeviceData(IConfiguration configuration, IUniqueDeviceIdentifierFactory uniqueDeviceIdentifierFactory,
ITokenIdentifierFactory tokenIdentifierFactory)
{
this.configuration = configuration;
this.uniqueDeviceIdentifierFactory = uniqueDeviceIdentifierFactory;
}
As you can see, you no longer need to make a call to the static Xamarin.Forms.DependencyService. Just ask for it in your Service or ViewModel constructor, and DEM will use the container to resolve the instance and provide it to you.
You can also gain access to Xamarin's DependencyService by using the IDevice interface. This interface removes the static call in your code, but still gives you access to Xamarin's DependencyService API.
public MainPageViewModel(IDeviceService deviceService)
{
var deviceId = deviceService.GetService<IUniqueDeviceIdentifierFactory>().GetId();
}