Getting Started with Autofac and Xamarin.Forms
I have had a strong desire lately to play around with Xamarin.Forms because I see it as a tool that would be great in my bag of tricks. There have been several apps that I have worked on in the past that I believe would align perfectly with what Forms does well but I did not have the comfort level to really take the dive. This post, along with a few others will chronicle that journey.
So first things first, I want my code to be clean and I want to be able to quickly take the shared logic from my app out of Forms and throw it into a Xamarin.iOS and Xamarin.Android apps if I feel like I cannot get Forms to work for me. My own little coded parachute. To that end what I want working right out of the gate is going to be an IoC (Inversion of Control) Container so that my code is nice and decoupled from the front end. I played around with a few different containers and for what I wanted I found Autofac to work well and it was a new container I had not used before. I know Xamarin.Forms has a nice dependency resolver but for my purposes I want to be able to inject my services into the constructor.
Getting Started
So to start I am just going to create a standard Xamarin.Forms project using a PCL for the backend. Update the NuGet packages and in my case I am just going to remove the UITest project/references. There is no need for them for this demo but we will visit those in a later post. Once that is done you can download Autofac from NuGet into the core, iOS and Android projects. Now we can go about setting up the app. These are the goals for this project:
- Build our app in an MVVM manner, with ViewModels for all of our pages.
- Inject our dependencies from the shared or platform specific projects
Setting up the Container
I like to start by creating a setup class, in this case called AppSetup that creates the container and registers the dependencies. We will set the dependency registration to virtual so that it can be overwritten in our platform specific projects.
public class AppSetup
{
public IContainer CreateContainer()
{
var containerBuilder = new ContainerBuilder();
RegisterDependencies(containerBuilder);
return containerBuilder.Build();
}
protected virtual void RegisterDependencies(ContainerBuilder cb)
{
cb.RegisterType<HomeViewModel>().SingleInstance();
}
}
And then I create the container itself.
public static class AppContainer
{
public static IContainer Container { get; set; }
}
Between the two of these I have pretty much everything I need to get started with my IoC Container. Now in my App.cs file for the initialization of Xamarin.Forms I can add in my AppSetup as a parameter to the constructor:
public App(AppSetup setup)
{
AppContainer.Container = setup.CreateContainer();
MainPage = new NavigationPage(new HomePage());
}
Now you just have to pass in the data from AppDelegate in iOS or from the MainActivity in Android and you're done with all of the setup. This would also work in a Windows Phone App but in my case I am using Xamarin Studio on a Mac so I do not have easy access to it.
Using the Container
For my pages I really like the way Rob Gibbens setup his project in his article Clean ViewModels with Xamarin.Forms. So that is what I'm going to show here. First of all, just create an empty interface for your ViewModel so that we have a little order in the world.
public interface IViewModel { }
Now we can create all of our ViewModels using this interface so that we have a way of finding all of them later on. For now, let's create the page so that everything is all wired up. To start we'll create the base type for our pages like Rob did:
public class ViewPage<T> : ContentPage where T:IViewModel
{
readonly T _viewModel;
public T ViewModel
{
get { return _viewModel; }
}
public ViewPage()
{
using (var scope = AppContainer.Container.BeginLifetimeScope())
{
_viewModel = AppContainer.Container.Resolve<T>();
}
BindingContext = _viewModel;
}
}
So all we're doing here is setting up our ViewPage so that it can resolve the ViewModel when it's created so that we don't need to worry about it later on. Now when we create a page it looks something like this:
public class HomePage : ViewPage<HomeViewModel>
{
public HomePage()
{
// Build the Home Page here...
}
}
Last, but certainly not least is injecting some dependencies so that we can see this working. So to start we'll create an interface in our shared project called IHelloFormsService:
public interface IHelloFormsService
{
string GetHelloFormsText();
}
Then we will create our implementations for our core, iOS and Android projects. You do not need to create one in the core project but I have for this demo so that I can remove the registration from the platform projects and see the core one is working. Below is the iOS implementation. You can see the rest in the source core which is linked below.
public class HelloFormsService : IHelloFormsService
{
public string GetHelloFormsText()
{
return "Hello iOS Forms!";
}
}
Next we'll create that HomeViewModel:
public class HomeViewModel : IViewModel
{
public HomeViewModel (IHelloFormsService formsService)
{
HelloText = formsService.GetHelloFormsText();
}
public string HelloText { get; set; }
}
Now we need to update our homepage so that it is using this HelloText property from the ViewModel. This is pretty simple since we already defined that ViewPage such that our Page knows which ViewModel it is referencing.
public HomePage ()
{
Content = new StackLayout {
Children = {
new Label { Text = ViewModel.HelloText }
}
};
}
Of course if you were to try and run the project now it would compile just fine and blow up once it tried to start. This is because we haven't actually registered this service with Autofac so we need to do that now. Instead of putting the registration into our core project this time we will need to create a new class that inherits from AppSetup inside of our iOS project. Once we've created the class we just overwrite RegisterDependencies to add in our iOS specific services. I recommend doing this after the base call that way if you decided to register IHelloFormsService
inside of your core project it would be overwritten by the last one registered, in this case, our iOS service. After all of that we then need to update our AppDelegate to pass along the new AppSetup into the App Constructor.
public class Setup : AppSetup
{
protected override void RegisterDependencies (ContainerBuilder cb)
{
base.RegisterDependencies(cb);
cb.RegisterType<HelloFormsService>().As<IHelloFormsService>();
}
}
Now if you were to run the app you should see a very nice Hello iOS Forms on the screen.
Source Code: GitHub