Rizwan Siddiqui
Rizwan Siddiqui

Reputation: 53

Dependency Injection in a xamarin forms cross platform mobile app (iOS, Android)

I am currently trying to integrate dependency injection into my xamarin forms cross platform mobile app using the Microsoft.Extensions.DependencyInjection library. I was able to register my services and viewmodels in my shared project, but I am unsure of how to register services that are implemented in platform specific projects. For example, I have an interface (IAuthenticationService) which is implemented through platform specific code, so it has implementations in my Android and iOS projects, but I'm not sure how to register that with my container so that the right implementation will be used based on the current running platform. This is how I set up my DI container in my startup class within my shared project:

public static class Startup
{
    public static IServiceProvider ServiceProvider { get; set; }

    public static IServiceProvider Init()
    {
        // Initialize all viewmodels and services with DI container
        var serviceProvider = new ServiceCollection().ConfigureServices().ConfigureViewModels().BuildServiceProvider();

        ServiceProvider = serviceProvider;

        return serviceProvider;
    }
}

public static class DependencyInjectionContainer
{
    
    public static IServiceCollection ConfigureServices(this IServiceCollection InServices)
    {
        //InServices.AddSingleton<IAuthenticationService>();
        InServices.AddSingleton<ISystemLayoutModel, SystemLayoutModel>();

        return InServices;
    }

    public static IServiceCollection ConfigureViewModels(this IServiceCollection InServices)
    {
        InServices.AddTransient<LoginViewModel>();
        InServices.AddTransient<StartViewModel>();
        InServices.AddTransient<RegistrationViewModel>();
        //All other viewmodels

        return InServices;
    }
}

And I call Init() in my App.xaml.cs to initialize the container with all the services and viewmodels:

public partial class App : Application
{
    public App()
    {            
        InitializeComponent();

        Startup.Init();

        MainPage = new AppShell();
    }
}

And everytime I want to inject a dependency into my viewmodel, I do something like this in the view code behind

    IStartViewModel _vM;

    public StartPage()
    {
        InitializeComponent();

        _vM = Startup.ServiceProvider.GetService<StartViewModel>();

        BindingContext = _vM;
    }

And the following viewmodel would look something like this:

class StartViewModel : ViewModelBase, IStartViewModel
{
    protected ISystemLayoutModel _LayoutModel;

    public StartViewModel(ISystemLayoutModel InSystemLayoutModel)
    {
        _LayoutModel = InSystemLayoutModel;

        LoadItemsForStart();

        if (_LayoutModel.SystemStartLayoutScreen.StartScreenTypes.Count < 1)
        {
            // Shutdown the application if it is not startup properly
            Application.Current.Quit();
        }
    }
    // More code
}

Upvotes: 2

Views: 2513

Answers (1)

ColeX
ColeX

Reputation: 14473

Official docs shows the way how to Register DependencyService implementations .

Interface

public interface IPhotoPicker
{
    Task<Stream> GetImageStreamAsync();
}

Android Implementation(constructor)

public PhotoPicker(Context context, ILogger logger)
{
    _context = context ?? throw new ArgumentNullException(nameof(context));
    _logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

On all the platforms, type registration with the dependency injection container is performed by the RegisterTypes method, which is invoked prior to the platform loading the application with the LoadApplication(new App()) method.

Android sample

void RegisterTypes()
{
    App.RegisterType<ILogger, Logger>();
    App.RegisterTypeWithParameters<IPhotoPicker, Services.Droid.PhotoPicker>(typeof(Android.Content.Context), this, typeof(ILogger), "logger");
    App.BuildContainer();
}

Call DependencyService.Resolve<T> somewhere , the dependency resolution method will be invoked to resolve the PhotoPicker type from the dependency injection container, which will also resolve and inject the Logger type into the PhotoPicker constructor.

async void OnSelectPhotoButtonClicked(object sender, EventArgs e)
{
    ...
    var photoPickerService = DependencyService.Resolve<IPhotoPicker>();
    var stream = await photoPickerService.GetImageStreamAsync();
    if (stream != null)
    {
        image.Source = ImageSource.FromStream(() => stream);
    }
    ...
}

Upvotes: 3

Related Questions