Reputation: 53
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
Reputation: 14473
Official docs shows the way how to Register DependencyService implementations .
public interface IPhotoPicker
{
Task<Stream> GetImageStreamAsync();
}
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.
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