Using Autofac with Xamarin

Good news is that Autofac, my IoC container of choice, works with Xamarin and even within Portable Class Library.  However there is one potential pitfall and it seems it happens at least when you use factories.

Imagine resolving ITubo defined as:

public class Tubo: ITubo
{
    public Tubo(Func<ITubo> tuboFactory)
    {}
}

public interface ITubo
{}

Note the use of factory through Func<ITubo>. In this case Autofac uses reflection and perhaps some runtime IL code generation. Now, try running the following piece of code on Xamarin.Android:

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Tubo>().As<ITubo>();
var container = builder.Build();
container.Resolve<ITubo>();

It is simple registration and after that I do resolve the interface. When you run this code it will most probably work. But that’s only because you are running it in Debug configuration. Now try running in in Release configuration. It will most probably throw an exception at you:

Autofac.Core.DependencyResolutionException: An exception was thrown while executing a resolve operation.

Not very informative but at least the call stack shows that problem happens within System.Core assembly. And it isn’t Autofac’s fault at all.

The reason for the problem is simple but perhaps not the most obvious one. Default project settings are that at Release configuration it cuts out all of the unused code from SDK libraries (Xamarin stuff which is basically all .net BCL) and combines all of the assemblies into a single file - through its linker (at Debug time it uses shared libraries as would .net on Windows). And since Autofac is doing operations at runtime (at least when it comes to factories), linker doesn’t see those and simply cuts off unused types at compilation time to reduce the output size. Read more about Xamarin linking process here:

Linking

The nasty part is that you won’t know what types are cut off until you run the application in Release configuration. Remember, always try your applications in Release configuration!

The solution

Anyway, the solution is, as I’ve found out, rather simple. Just instruct linker to leave alone (don’t optimize) System.Core assembly. If you are using Visual Studio the go to Project/Properties, Android Options tab and enter System.Core text into Skip linking assemblies text box. If you are using Xamarin IDE the go to Project/Properies, Build/Android Build tab, Linker subtab and enter the same System.Core into Ignore Assemblies text box (didn’t try this one). That’s it, linker will leave alone that assembly and Autofac will happily run even in Release configuration.

Luckily the difference in output file size isn’t that significant: 450KB in worst case.

Invoking DatePickerDialog asynchronously with Xamarin.Android

Invoking a DatePickerDialog in Xamarin.Android is a bit tricky because it is done through callbacks and at the end one has to dispose it as well. Hence I present asynchronous extension methods PickDateAsync and PickDateAndAssignAsync.

With my code you can use this syntax and don’t worry about DatePickerDialog disposal:

EditText et1 = FindViewById<EditText>(Resource.Id.editText1);
await et1.PickDateAndAssignAsync();

There is also an overload that accepts an instance of CancellationToken. Here are the methods:

public static class DatePickerExtension
{
    public static Task<DateTime> PickDateAsync(this EditText dateEdit)
    {
        return dateEdit.PickDateAsync(CancellationToken.None);
    }
    public static Task<DateTime> PickDateAsync(this EditText dateEdit, CancellationToken ct)
    {
        TaskCompletionSource<DateTime> tsc = new TaskCompletionSource<DateTime>();
        DatePickerDialog picker = null;
        DateTime current;
        if (!DateTime.TryParse(dateEdit.Text, out current))
            current = DateTime.Now;
        picker = new DatePickerDialog(dateEdit.Context, (sender, e) =>
        {
            if (tsc != null)
            {
                if (ct.IsCancellationRequested)
                {
                    tsc.SetCanceled();
                }
                else
                {
                    tsc.SetResult(e.Date);
                }
                tsc = null;
                picker.Dispose();
            }
        }, current.Year,current.Month-1,current.Day);
        picker.Show();
        return tsc.Task;
    }

    public static Task PickDateAndAssignAsync(this EditText dateEdit)
    {
        return dateEdit.PickDateAndAssignAsync(CancellationToken.None);
    }
    public static async Task PickDateAndAssignAsync(this EditText dateEdit, CancellationToken ct)
    {
        DateTime date = await dateEdit.PickDateAsync(ct);
        if (!ct.IsCancellationRequested)
        {
            dateEdit.Text = date.ToString("d");
        }
    }
}
Enjoy awesome asynchronous C#/.net support.