Registering dependencies

As mentioned earlier, each IoC and dependency injection library implements the patterns slightly differently. In this section, we will use Ninject to start adding dependency injection capabilities to our TripLog app. Ninject allows you to create Modules, which are responsible for adding services to the IoC container. The modules are then added to a Kernel that is used to resolve the services by other areas of the app.

You can create a single Ninject Module or many, depending on how your app is structured and how you want to organize your services. For the TripLog app, we will have a Ninject Module in each platform project that is responsible for registering that platform's specific service implementations. We will also create a Ninject Module in the core library that will be responsible for registering dependencies that live in the core library, such as ViewModels and data access services, which we will add later in Chapter 6, API Data Access, when we start working with live data.

Registering the platform-service implementations

We will start by creating Ninject Modules in each of the platform projects that will be responsible for registering their respective platform's specific service implementations, as shown in the following steps:

  1. Add the Portable.Ninject NuGet package to each of the platform-specific projects—TripLog.iOS and TripLog.Droid.
  2. Next, create a new folder in the TripLog.iOS project named Modules, and within it, create a new class named TripLogPlatformModule that inherits from Ninject.Modules.NinjectModule:
    public class TripLogPlatformModule : NinjectModule
    {
        // ...
    }
  3. Override the Load method of the NinjectModule class and use the Ninject Bind method to register the iOS-specific implementation of ILocationService as a singleton:
    public class TripLogPlatformModule : NinjectModule
    {
        public override void Load ()
        {
            Bind<ILocationService> ()
            .To<LocationService> ()
            .InSingletonScope ();
        }
    }
  4. Next, create a folder in the TripLog.Droid project named Modules, and within it, create a new class named TripLogPlatformModule that inherits from Ninject.Modules.NinjectModule:
    public class TripLogPlatformModule : NinjectModule
    {
        // ...
    }
  5. Finally, override the Load method of the NinjectModule class and use the Ninject Bind method to register the Android-specific implementation of ILocationService as a singleton:
    public class TripLogPlatformModule : NinjectModule
    {
        public override void Load ()
        {
            Bind<ILocationService> ()
            .To<LocationService> ()
            .InSingletonScope ();
        }
    }

Registering the ViewModels

We can also use our IoC container to hold our ViewModels. It is a slightly different model than the one used to register concrete implementations of our service interfaces—instead of mapping them to an interface, we are simply registering them to themselves.

Because our ViewModels are in our core library, we will create another Ninject Module in the core library, a core module, that will handle registering them, as shown in the following steps:

  1. Add the Portable.Ninject NuGet package to the core project.
  2. Create a new folder in the core project named Modules, and within it, create a new class named TripLogCoreModule that inherits from Ninject.Modules.NinjectModule:
    public class TripLogCoreModule : NinjectModule
    {
        // ...
    }
  3. Override the Load method of the NinjectModule class and use the Ninject Bind method to register each of the ViewModels:
    public class TripLogCoreModule : NinjectModule
    {
        public override void Load ()
        {
            // ViewModels
            Bind<MainViewModel> ().ToSelf ();
            Bind<DetailViewModel> ().ToSelf ();
            Bind<NewEntryViewModel> ().ToSelf ();
        }
    }

Registering the navigation service

In the previous chapter, we created a custom navigation service and used the Xamarin.Forms DependencyService to register and resolve it. Now that we have introduced Ninject, we can swap Xamarin.Forms DependencyService out and use a Ninject Module to register the navigation service instead so that it can be resolved and used just like our platform-specific services.

  1. The only change we need to make to the navigation service itself is to remove the assembly attribute that was added above the class's namespace:
    // Remove assembly attribute
    // [assembly: Dependency(typeof(XamarinFormsNavService))]

    We originally instantiated the navigation service and registered view mappings all within the core App class. We can now move all of that logic into a new Ninject Module. However, in order for us to instantiate our navigation service, we require an instance of Xamarin.Forms.INavigation, so we will have to set this new Module up to take that in as a constructor parameter, and then its overridden Load method will handle creating the service, creating the view mappings, and then registering the service into the IoC.

  2. Create a new class in the core project Modules folder named TripLogNavModule that inherits from NinjectModule:
    public class TripLogNavModule : NinjectModule
    {
        // ...
    }
  3. Update the constructor of the TripLogNavModule to take in a Xamarin.Forms.INavigation parameter:
    public class TripLogNavModule : NinjectModule
    {
        readonly INavigation _xfNav;
    
        public TripLogNavModule (INavigation xamarinFormsNavigation)
        {
            _xfNav = xamarinFormsNavigation;
        }
    }
  4. Override the Load method of the NinjectModule class to instantiate a new XamarinFormsNavService object:
    public override void Load ()
    {
        var navService = new XamarinFormsNavService ();
        navService.XamarinFormsNav = _xfNav;
    }
  5. Remove the ViewModel-to-View mappings from the App class and put them into the TripLogNavModule Load override method:
    public override void Load ()
    {
        var navService = new XamarinFormsNavService ();
        navService.XamarinFormsNav = _xfNav;
    
        // Register view mappings
        navService.RegisterViewMapping (
          typeof(MainViewModel),
          typeof(MainPage));
    
        navService.RegisterViewMapping (
            typeof(DetailViewModel),
            typeof(DetailPage));
    
        navService.RegisterViewMapping (
              typeof(NewEntryViewModel),
              typeof(NewEntryPage));
    }
  6. Finally, update the TripLogNavModule Load override method to use the Ninject Bind method to register the XamarinFormsNavService object as a singleton:
    public override void Load ()
    {
        var navService = new XamarinFormsNavService ();
        navService.XamarinFormsNav = _xfNav;
    
        // Register view mappings
        navService.RegisterViewMapping (
            typeof(MainViewModel),
            typeof(MainPage));
    
        navService.RegisterViewMapping (
            typeof(DetailViewModel),
            typeof(DetailPage));
    
        navService.RegisterViewMapping (
            typeof(NewEntryViewModel),
            typeof(NewEntryPage));
    
        Bind<INavService>
     ()
            .ToMethod (x => navService)
            .InSingletonScope ();
    }

    Tip

    Platform-specific services are good candidates for singleton objects. ViewModels can be singletons, but typically should not be.

Updating the TripLog app

Now that our platform services, navigation service, and ViewModels have all been registered with the IoC, we need to add the Ninject Modules that we created to the Ninject Kernel. We will do this in our main Xamarin.Forms.Application class, App.

In order to get our platform modules into the App class, which is in our core library, we will simply update the App constructor to take in INinjectModule parameters. Then, each platform-specific project will be responsible to pass in its respective Ninject module when it loads the App at startup.

  1. Update the App constructor to take in INinjectModule parameters:
    public App (params INinjectModule[] platformModules)
    {
        // ...
    }
  2. Next, add a public IKernel property named Kernel to the App class:
    public class App : Application
    {
        public IKernel Kernel { get; set; }
    
        // ...
    }
  3. Next, update the body of the App constructor. In the previous section, we moved the bulk of the existing App constructor logic into the navigation Ninject Module. Now the App constructor should only be responsible for creating the main page and initializing the Ninject Kernel with the various modules we have created:
    public App (params INinjectModule[] platformModules)
    {
        var mainPage = new NavigationPage (new MainPage ());
    
        // Register core services
        Kernel = new StandardKernel (
            new TripLogCoreModule (),
            new TripLogNavModule(mainPage.Navigation));
    
        // Register platform specific services
        Kernel.Load (platformModules);
    
        // Get the MainViewModel from the IoC
        mainPage.BindingContext = Kernel.Get<MainViewModel> ();
    
        MainPage = mainPage;
    }

    Notice how we get an instance of the MainViewModel from the IoC container and use it to set the ViewModel of the MainPage. In the next section, we'll update the navigation service to do this same thing each time we navigate to the other pages in the app.

  4. Finally, we need to update the App instantiation in the AppDelegate class of our iOS project to pass in a new instance of TripLog.iOS.Modules.TripLogPlatformModule:
    LoadApplication (new App (new TripLogPlatformModule()));
  5. The same thing needs to be done in the MainActivity class of the Android project.

Updating the navigation service to handle ViewModel creation and dependency injection

Currently in the TripLog app, each page is responsible for creating its own ViewModel instance. However, because we provide a ViewModel's dependencies through its constructor, we would have to manually resolve each dependency within the Page class and pass them into the ViewModel instantiation. Not only is this going to be messy code, it is also difficult to maintain and doesn't promote loose coupling. Because we have registered our ViewModels in our IoC, we can completely remove the ViewModel instantiations from our Pages and set our navigation service up to handle resolving the ViewModels from the IoC, automatically supplying their dependencies through constructor injection.

  1. First, remove the code from the constructor of each Page that sets its BindingContext property to a new ViewModel instance.
  2. Next, update the NavigateToView private method in the XamarinFormsNavService to handle setting the ViewModels of the Pages automatically as they are navigated to. After the Page (view) is created using the Invoke method, simply get a new instance of the specified ViewModel and set it to the BindingContext of the Page:
    async Task NavigateToView(Type viewModelType)
    {
        // ...
    
        var view = constructor.Invoke (null) as Page;
    
        var vm = ((App)Application.Current).Kernel.GetService (viewModelType);
        view.BindingContext = vm;
    
        await XamarinFormsNav.PushAsync (view, true);
    }
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.250.11