Dependency injection using Shell in MAUI

The MauiAppBuilder (as seen in the MauiProgram class) i.e.

var builder = MauiApp.CreateBuilder();

exposes the property Services which is a collection of ServiceDescriptor objects. This is where we register our “services”. We can use AddSingleton or AddTransient or AddScoped.

So for example

builder.Services.AddSingleton<MainPage>();
builder.Services.AddSingleton<MainPageViewModel>();
  • AddSingleton
    As the name suggests, a single instance of a type is created across the lifetime of the application.
  • AddTransient
    A new instance of a type is created for each request, the equivalent of creating a new instance for every type that has a dependency, hence each gets a unique instance.
  • AddScoped
    A new instance of a type is created for a request and all dependencies within that request gets the same instance. For different requests a new instance is created, hence unique across requests.

Whilst the IoC container with MAUI Shell, isn’t as comprehensive in features as some containers, such as Autofac, it’s good enough for many. It does include constructor dependency injection so we will probably fulfil most use cases.

Okay, let’s assume with have a MainPage and on that we have a button which displays an AboutPage. As per the code above suggests. We can create a view model for each page, so we have a MainPageViewModel and AboutPageViewModel, then within the actual .xaml.cs of those pages, in the constructor we have code such as

public MainPage(MainPageViewModel vm)
{
   InitializeComponent();
   BindingContext = vm;
}


public AboutPage(AboutPageViewModel vm)
{
   InitializeComponent();
   BindingContext = vm;
}

Now all we need to do is register the pages and view models within the MauiProgram with the builder, so we have

builder.Services.AddSingleton<MainPage>();
builder.Services.AddSingleton<MainPageViewModel>();
builder.Services.AddTransient<AboutPage>();
builder.Services.AddTransient<AboutPageViewModel>();

When Maui starts up and loads MainPage.xaml it will inject the MainPageViewModel and also when we navigate to our AboutPage using something like the code below, again the view model is injected

[RelayCommand]
private async Task About() => await AppShell.Current.GoToAsync(nameof(AboutPage));

Note: The example uses the MVVM Community Toolkit, hence the RelayCommandAttribute.

We may need to resolve dependencies ourselves (in other words we’re unable to use the magic of something like constructor injection). Rather strangely I would have expected MAUI to already have code within for this, but it seems that current the solution is to create (or use an existing) ServiceProvider class. For example, the one shown below is taken from David Ortinau’s sample.

public static class ServiceProvider
{
   public static TService GetService<TService>()
      => Current.GetService<TService>();

   public static IServiceProvider Current
      =>
#if WINDOWS10_0_17763_0_OR_GREATER
	MauiWinUIApplication.Current.Services;
#elif ANDROID
        MauiApplication.Current.Services;
#elif IOS || MACCATALYST
	MauiUIApplicationDelegate.Current.Services;
#else
	null;
#endif
}

Now now we can use the static class ServiceProvider to get the Services, for example let’s remove the dependency injection in the AboutPage constructor and instead use the ServiceProvider like this

BindingContext = ServiceProvider.GetService<AboutPageViewModel>();

Ofcourse this is not so easily testable, but the option is there if required.