Implementing Native Camera Capture in Xamarin.Forms with DependencyService
Learn how to add camera capture to a Xamarin.Forms app using DependencyService, handle permissions, and verify the result on Android and iOS.
19 Mar 2026, 16:38 UTC

Problem Statement
You want a single button in your Xamarin.Forms UI that opens the device camera, lets the user take a photo, and returns the image to the shared code. The UI must stay platform‑agnostic, while the camera implementation is platform‑specific. Xamarin’s DependencyService lets you expose a clean interface to the shared layer and register concrete classes in each native project.
Prerequisites
- Visual Studio 2022 or later with Xamarin workloads.
- Target SDK 33+ for Android, iOS 16+ for iOS.
- Project set to .NET Standard 2.0+ (required for Xamarin.Essentials).
- Camera hardware on the test device; emulators often lack full camera support.
- Basic knowledge of C#, XAML, and platform manifest files.
Shared Interface
Define an interface that represents the camera service. Place it in the shared project so both platforms can reference it.
public interface ICameraService
{
Task<Stream> TakePhotoAsync();
}
Use Task<Stream> to return the raw image data; callers can decide how to display or store it.
Android Implementation
In the Android project create a class that implements ICameraService and register it in App.xaml.cs during startup.
using Android.App;
using Android.Content;
using Android.OS;
using Android.Provider;
using System.IO;
using System.Threading.Tasks;
using Xamarin.Forms;
[assembly: Dependency(typeof(CameraService))]
namespace YourApp.Droid
{
public class CameraService : ICameraService
{
public async Task<Stream> TakePhotoAsync()
{
// 1. Ensure permission
if (CheckSelfPermission(Android.Manifest.Permission.Camera) != Permission.Granted)
{
RequestPermissions(new[] { Android.Manifest.Permission.Camera }, 0);
return null;
}
// 2. Create intent
var intent = new Intent(MediaStore.ActionImageCapture);
var tempFile = CreateTempFile();
var uri = FileProvider.GetUriForFile(this, "com.yourapp.fileprovider", tempFile);
intent.PutExtra(MediaStore.ExtraOutput, uri);
// 3. Start activity for result
var activity = Platform.CurrentActivity;
var result = await activity.StartActivityForResultAsync(intent, 0);
if (result.ResultCode != Result.Ok) return null;
// 4. Read file into stream
var stream = File.OpenRead(tempFile.AbsolutePath);
return stream;
}
private Java.IO.File CreateTempFile()
{
var storageDir = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures);
if (!storageDir.Exists()) storageDir.Mkdirs();
return Java.IO.File.CreateTempFile("photo_", ".jpg", storageDir);
}
}
}
Key points:
- Register with
[assembly: Dependency(typeof(CameraService))]for case‑sensitive matching. - Use
FileProviderto expose the file URI to the camera app. - Always check
Permission.Grantedbefore launching the intent. - Handle
Result.Okto confirm the photo was taken.
iOS Implementation
On iOS use the AVFoundation framework or, simpler, Xamarin.Essentials.MediaPicker. Below shows a native implementation via AVCaptureSession for illustration.
using AVFoundation;
using Foundation;
using System.IO;
using System.Threading.Tasks;
using Xamarin.Forms;
[assembly: Dependency(typeof(CameraService))]
namespace YourApp.iOS
{
public class CameraService : ICameraService
{
public Task<Stream> TakePhotoAsync()
{
var tcs = new TaskCompletionSource<Stream>();
var picker = new UIImagePickerController
{
SourceType = UIImagePickerControllerSourceType.Camera,
MediaTypes = new string[] { "public.image" }
};
picker.FinishedPickingMedia += (s, e) => {
var image = e.OriginalImage;
var data = image.AsJPEG();
var stream = new MemoryStream(data.ToArray());
tcs.SetResult(stream);
picker.DismissViewController(true, null);
};
picker.Canceled += (s, e) => {
tcs.SetResult(null);
picker.DismissViewController(true, null);
};
var vc = UIApplication.SharedApplication.KeyWindow.RootViewController;
vc.PresentViewController(picker, true, null);
return tcs.Task;
}
}
}
Remember to add NSCameraUsageDescription to Info.plist with a user‑friendly string; otherwise the app crashes on launch when the camera is accessed.
Using Xamarin.Essentials MediaPicker (Alternative)
If you prefer a higher‑level API that handles permissions automatically, replace the DependencyService with Xamarin.Essentials.MediaPicker.TakePhotoAsync. Add the NuGet package to all projects and call it directly from shared code:
using Xamarin.Essentials;
public async Task<Stream> TakePhotoAsync()
{
var photo = await MediaPicker.CapturePhotoAsync();
if (photo == null) return null;
var stream = await photo.OpenReadAsync();
return stream;
}
Note: Ensure your app targets .NET Standard 2.0+; older projects need to be upgraded.
Runtime Permission Handling
Android 6.0+ requires runtime permission requests. The Android implementation above calls RequestPermissions but you should also override OnRequestPermissionsResult in MainActivity.cs to forward the result to the DependencyService:
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
On iOS permissions are requested automatically when UIImagePickerController is presented; the system displays the prompt based on NSCameraUsageDescription.
Testing and Validation
- Deploy to a physical device: emulators often lack camera support.
- Invoke the camera button from the UI. Verify the camera app opens.
- Capture a photo. After dismissal, the image should appear in the app’s
Imageview. - Check device storage: on Android the file should be in
Pictures; on iOS it should appear in the Photos library. - Logcat / Console: ensure no permission denial messages or crashes.
- Unit test: mock
ICameraServiceand assert thatTakePhotoAsyncis called when the UI command executes.
Recovery and Cleanup
- If the user cancels the capture,
TakePhotoAsyncreturnsnull. Handle this case gracefully by displaying a toast or dialog. - On Android, delete temporary files after they are read to avoid filling the external storage. Add a cleanup step in the service:
File.Delete(tempFile.AbsolutePath);
UnauthorizedAccessException and IOException to report permission or storage issues to the user.Summary
By defining a simple ICameraService interface and providing platform‑specific implementations registered with DependencyService, you keep the UI code clean and testable. Remember to:
- Register the service correctly and handle case‑sensitivity.
- Request runtime permissions on Android and include
NSCameraUsageDescriptionon iOS. - Test on real devices and verify the image appears in the app and device storage.
- Consider using
Xamarin.Essentials.MediaPickerfor a simpler, permission‑aware approach.
With these steps, your Xamarin.Forms app can reliably capture photos across Android and iOS while maintaining a clean separation between shared UI and native functionality.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.