Adventures in Xamarin.Forms: Xamarin.Forms.DependencyService

This is the first installment of a semi regular series of posts chronicling my attempt at learning Xamarin.Forms. It will be a bumpy and messy ride, so wear your seat belts and bring a change of clothes. But not only will this be my first steps in Xamarin brand big-boy shoes, it is also my first time using a Mac, so yeah, lets get started!

NOTE: As I pay for my own license, I could only afford iOS for now. In the future depending on whether I decide to make a go at this for more than a learning exercise I might pickup the android license as well.

Today I will be talking about the DependencyService, and a couple of quirks I ran into while implementing some native functionality in a Xamarin Forms app.

DependencyService

DependencyService is a dependency injection library allowing you to call native code from your portable class library. It is set up by defining an interface in your PCL project, implementing that interface on your platform-specific project, registering it then using DependencyService.Get<T>() to return the correct implementation of the interface.

Let’s get started with a real world example. This is something I actually needed to implement for my own project.

Popup Input Box

In Xamarin.Forms, it seems there is no built in way to pop the AlertBox with a Entry field in it, so you will need to use the native implementation of UIAlertin your iOS project.

Let’s define our interface.

using System;
using System.Threading.Tasks;

namespace AdventuresInXamarinForms1
{
    public interface IPrompt
    {
        Task<string> DisplayPrompt(string title, string message);
    }
}

As you can see there is nothing special about this interface. It is called IPromptand it defines a DisplayPrompt method that returns a Task for reasons we will get to in a little bit.

So far, this DependencyService thing is turning out to be pretty simple!

Now we will need to define our platform specific implementation. As I am only on iOS with my Xamarin.License, I will only be defining the iOS implementation.

using System;
using System.Threading.Tasks;

using UIKit;
using Foundation;

namespace AdventuresInXamarinForms1.iOS
{
    public class Prompt_iOS : IPrompt
    {
        public Prompt_iOS () { }

        public Task<string> DisplayPrompt(string title, string message) {
            var ret = new TaskCompletionSource<string> ();

            UIAlertView alert = new UIAlertView (title, message, null, "Cancel", "OK");
            alert.AlertViewStyle = UIAlertViewStyle.PlainTextInput;

            alert.Clicked += (sender, e) => {
                if (e.ButtonIndex != alert.CancelButtonIndex)
                    ret.SetResult (alert.GetTextField (0).Text);
                else
                    ret.SetResult (null);
            };

            alert.ShouldEnableFirstOtherButton += (UIAlertView alertView) => {
                return !string.IsNullOrWhiteSpace (alertView.GetTextField (0).Text);
            };

            alert.Show ();

            return ret.Task;
        }
    }
}

The logic here might not be as immediately apparent to people who haven’t use async/await yet, but what the DisplayPrompt method is doing is creating a TaskCompletionSource object to watch for the results of the alert.Clicked event. So not to block the main thread, we return back the watcher to the caller.

Now that the interface is implemented, we will need to register this class in the DependencyService. There are a couple ways I know to do this.

The first one is using the [assembly] attribute:

[assembly: Xamarin.Forms.Dependency (typeof (Prompt_iOS))]

The other is the manual option in the AppDelegate.cs file in the platform project.

public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
    global::Xamarin.Forms.Forms.Init ();
    Xamarin.Forms.DependencyService.Register<Prompt_iOS> ();

    LoadApplication (new App ());

    return base.FinishedLaunching (app, options);
}

I’m partial to the second method, because you register everything in a single place, so you know what is and isn’t registered. In a real world project, you would want move all of the registrations out of the AppDelegate.cs and into there own file to keep your code separate from the Xamarin generated code.

And the last step is to actually call the DependencyService.Get() method. In this example, I am calling the code from a button.Clicked event inside my main ContentPage file that was generated at the start of the project. This code could go almost anywhere in the project, you might even extrapolate it out so you aren’t calling DependencyService.Get() every time you need to you it. If you use the same multiple lines of code more than twice, move it to a shared method.

button.Clicked += async (sender, e) => {
    var prompt = DependencyService.Get<IPrompt> ();
    this.Title = await prompt.DisplayPrompt ("Title", "Message");
};

Summary

Today we learned one way to implement dependency injection in our Xamarin.Forms portable class library using the Xamarin.Forms.DependencyService with the ultimate goal of calling native code. As with skinning cats, there is more than one way to handle dependency injection, this is just the built in Xamarin way.

If you found this post useful, please share it!

Leave a Reply