Handling Platform-Specific APIs in Xamarin.Forms with DependencyService
Learn how to bridge the gap between shared .NET code and native Android/iOS APIs using the DependencyService pattern in Xamarin.Forms.
06 Jul 2026, 12:26 UTC

The Gap Between Shared Code and Native APIs
When building a Xamarin.Forms application, the goal is to maximize shared code. However, you eventually hit a wall: the shared .NET Standard project cannot directly call native APIs like the Android SensorManager or iOS CoreMotion because the shared project is platform-agnostic.
The solution is the DependencyService. This is a service locator that allows the shared project to define what needs to happen via an interface, while the platform projects define how it happens using native code. The takeaway is simple: use DependencyService to decouple your business logic from the underlying operating system.
Defining the Contract
The process begins in the shared project. You must create an interface that describes the functionality you need. This interface acts as the bridge; the shared project only knows about the interface, not the implementation.
For example, if you need to check the device's battery level—a feature that requires different APIs on Android and iOS—you define an IBatteryService interface in your shared project.
Implementing and Registering the Service
Once the interface exists, you must provide a concrete implementation in each platform project (e.g., .Android and .iOS). The critical step here is registration. Xamarin.Forms needs to know which class to instantiate when the shared project asks for the interface.
This is done using the [assembly: Dependency(typeof(ClassName))] attribute. This attribute must be placed outside the namespace declaration at the top of the file. Without this registration, DependencyService.Get<T>() will return null at runtime.
Worked Example: Device Orientation
Here is a practical implementation for retrieving the device orientation.
1. Shared Project:
public interface IDeviceOrientation
{
string GetOrientation();
}
2. Android Project (Implementation):
[assembly: Xamarin.Forms.Dependency(typeof(YourApp.Droid.AndroidOrientation))]
namespace YourApp.Droid
{
public class AndroidOrientation : IDeviceOrientation
{
public string GetOrientation()
{
// Simplified example using Android native logic
return "Android Portrait/Landscape";
}
}
}
3. iOS Project (Implementation):
[assembly: Xamarin.Forms.Dependency(typeof(YourApp.iOS.IosOrientation))]
namespace YourApp.iOS
{
public class IosOrientation : IDeviceOrientation
{
public string GetOrientation()
{
// Simplified example using iOS native logic
return "iOS Portrait/Landscape";
}
}
}
4. Consuming in the ViewModel:
var orientationService = DependencyService.Get<IDeviceOrientation>();
string currentOrientation = orientationService?.GetOrientation() ?? "Unknown";
Trade-offs and Limitations
While DependencyService is convenient, it is a Service Locator pattern, not true Dependency Injection (DI). This introduces a few engineering challenges:
- Unit Testing: Because
DependencyService.Get<T>()is a static call, it is difficult to mock the service in unit tests without wrapping the locator in another abstraction. - Tight Coupling: Over-reliance on this pattern can lead to "leaky abstractions," where platform-specific logic starts bleeding into the shared project's architecture.
- Lifecycle Management: By default,
DependencyServicecreates a singleton instance of the implementation. If your service needs to be recreated or depends on a short-lived activity context, you will need to manage that state manually.
Verification and Result Checking
To verify the implementation, run the application on a physical device (emulators often return generic or null values for hardware sensors). If the app crashes with a NullReferenceException when calling the service, check that the [assembly: Dependency] attribute is correctly placed outside the namespace in the platform project.
Rollback: If the implementation causes stability issues, remove the DependencyService.Get<T>() call in the shared project and delete the implementation classes in the platform projects to return the app to its previous state.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.