Nathan Evans' Nemesis of the Moment

Xamarin/MvvmCross + Autofac

Posted in .NET Framework, Software Design, Xamarin by Nathan B. Evans on February 17, 2014

Recently I’ve been doing some Xamarin development and naturally once a mobile app project reaches a certain size you need to factor it away from just a hacky prototype app towards a sustainable design that ticks all the SOLID boxes.

I researched a handful of various approaches to adopting the MVVM pattern, which included:

  • MvvmCross (OSS, also known as “Mvx”)
  • ReactiveUI (OSS, built on top of the excellent Reactive Extensions / RX library)
  • Crosslight (commercial)

At this time the project didn’t warrant spending $999 on Crosslight (and I only assume it must be very good at that price). So I veered towards an OSS solution. ReactiveUI is by far the more elegantly designed when compared to MvvmCross. However its mobile platform support is relatively new and very focused on solving only the MVVM problem. MvvmCross however is more of a framework that helps you in a number of different ways concerning mobile app development, including providing a dependency injection / IOC layer, and numerous platform-specific extensions for Android, iOS etc. MvvmCross is however, in my opinion, a little “rough around the edges” and most definitely has been put together more like a Panzer Tank than a Lotus Elise.

Ultimately I adopted MvvmCross though as it has the greatest level of momentum in the Xamarin ecosystem and this is important I feel.

One big issue with MvvmCross is that it seems to take on the responsibility of providing a really rather crap IOC container implementation. I’m not sure why it doesn’t just depend upon Autofac or TinyIoc or something like that as it seems like 30% of the code in the MvvmCross codebase could be stripped out if it just farmed out that responsibility to another OSS project. Literally everywhere you look in the MvvmCross codebase there are “factory” and “registry” and, uh, “singleton” components everywhere. Maybe Autofac has spoilt me over the years but I honestly can’t remember the last time I had to “hand roll” such a boilerplate component.

So I set about solving this problem by writing a Autofac adapter for MvvmCross. It turned out to be a lot simpler than I first thought, after working through various nuances of MvvmCross.

AutofacMvxIocProvider.cs

I chose to place this type in a separate assembly intended for all my “Autofac for MvvmCross” related extensions. It is a PCL assembly, since Autofac is fully PCL compatible, even with Xamarin.

public class AutofacMvxIocProvider : MvxSingleton<IMvxIoCProvider>, IMvxIoCProvider {
    private readonly IContainer _container;

    public AutofacMvxIocProvider(IContainer container) {
        if (container == null) throw new ArgumentNullException("container");
        _container = container;
    }

    public bool CanResolve<T>() where T : class {
        return _container.IsRegistered<T>();
    }

    public bool CanResolve(Type type) {
        return _container.IsRegistered(type);
    }

    public T Resolve<T>() where T : class {
        return (T)Resolve(typeof(T));
    }

    public object Resolve(Type type) {
        return _container.Resolve(type);
    }

    public T Create<T>() where T : class {
        return Resolve<T>();
    }

    public object Create(Type type) {
        return Resolve(type);
    }

    public T GetSingleton<T>() where T : class {
        return Resolve<T>();
    }

    public object GetSingleton(Type type) {
        return Resolve(type);
    }

    public bool TryResolve<T>(out T resolved) where T : class {
        return _container.TryResolve(out resolved);
    }

    public bool TryResolve(Type type, out object resolved) {
        return _container.TryResolve(type, out resolved);
    }

    public void RegisterType<TFrom, TTo>()
        where TFrom : class
        where TTo : class, TFrom {

        var cb = new ContainerBuilder();
        cb.RegisterType<TTo>().As<TFrom>().AsSelf();
        cb.Update(_container);
    }

    public void RegisterType(Type tFrom, Type tTo) {
        var cb = new ContainerBuilder();
        cb.RegisterType(tTo).As(tFrom).AsSelf();
        cb.Update(_container);
    }

    public void RegisterSingleton<TInterface>(TInterface theObject) where TInterface : class {
        var cb = new ContainerBuilder();
        cb.RegisterInstance(theObject).As<TInterface>().AsSelf().SingleInstance();
        cb.Update(_container);
    }

    public void RegisterSingleton(Type tInterface, object theObject) {
        var cb = new ContainerBuilder();
        cb.RegisterInstance(theObject).As(tInterface).AsSelf().SingleInstance();
        cb.Update(_container);
    }

    public void RegisterSingleton<TInterface>(Func<TInterface> theConstructor) where TInterface : class {
        var cb = new ContainerBuilder();
        cb.Register(cc => theConstructor()).As<TInterface>().AsSelf().SingleInstance();
        cb.Update(_container);
    }

    public void RegisterSingleton(Type tInterface, Func<object> theConstructor) {
        var cb = new ContainerBuilder();
        cb.Register(cc => theConstructor()).As(tInterface).AsSelf().SingleInstance();
        cb.Update(_container);
    }

    public T IoCConstruct<T>() where T : class {
        return (T)IoCConstruct(typeof(T));
    }

    public object IoCConstruct(Type type) {
        return Resolve(type);
    }

    public void CallbackWhenRegistered<T>(Action action) {
        CallbackWhenRegistered(typeof(T), action);
    }

    public void CallbackWhenRegistered(Type type, Action action) {
        _container.ComponentRegistry.Registered += (sender, args) => {
            if (args.ComponentRegistration.Services.OfType<TypedService>().Any(x => x.ServiceType == type)) {
                action();
            }
        };
    }
}

Setup.cs

I’m just showing my MvxAndroidSetup implementation here, but your iOS and Windows Phone etc would obviously look basically the same.

public class Setup : MvxAndroidSetup {
    private static Assembly CoreAssembly { get { return typeof(App).Assembly; } }

    public Setup(Context applicationContext) : base(applicationContext) { }

    protected override IMvxApplication CreateApp() {
        return new App();
    }

    protected override IMvxIoCProvider CreateIocProvider() {
        var cb = new ContainerBuilder();

        // I like to structure my app using Autofac modules.
        // It keeps everything very DRY and SRP compliant.
        // Ideally, these Autofac modules would be held in a separate PCL so they can be used
        // by Android / iOS / WP platforms without violating DRY.
        cb.RegisterModule<InfrastructureModule>();
        cb.RegisterModule<FeaturesModule>();

        // This is an important step that ensures all the ViewModel's are loaded into the container.
        // Without this, it was observed that MvvmCross wouldn't register them by itself; needs more investigation.
        cb.RegisterAssemblyTypes(CoreAssembly)
            .AssignableTo<MvxViewModel>()
            .As<IMvxViewModel, MvxViewModel>()
            .AsSelf();

        return new AutofacMvxIocProvider(cb.Build());
    }
}

Conclusion

This enables me to use Autofac unhindered from my Xamarin mobile apps. It allows the codebase to remain consistent by only using one IOC container, which helps minimise complexity, encourages more DRY code and in the future would lower the barriers to getting more developers up to speed with the whole codebase. Autofac is by far the best IOC container available for .NET and having it available for using in Xamarin when coupled with MvvmCross provides a major improvement in productivity for me.

5 Responses

Subscribe to comments with RSS.

  1. Виктор Фефилов said, on March 28, 2014 at 9:50 AM

    Thank you very much, a very useful article! However it would be great if you’ll attach a working sample. Can’t make your code work for my case.

  2. James Ottaway said, on April 22, 2014 at 11:04 PM

    Hey Nathan, I’d love your permission to contribute your AutofacMvxIocProvider class to the Extras section of Autofac. Do you have a specific license you’re happy to release this code under?

    • Nathan B. Evans said, on April 23, 2014 at 9:32 AM

      Hi James,

      Absolutely, that’s fine. Is MIT license okay for you?

      Thanks.

      • James Ottaway said, on April 23, 2014 at 11:00 PM

        Yeah, that’s perfect. I’ll link back to this blog post in the PR as a reference to the license. Thanks!

  3. tofutim said, on May 29, 2015 at 10:41 PM

    Nathan, this looks like what I want to do. Thanks for the post.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: