Creating and using platform-specific services

We already created a service in the preceding chapter to handle navigation. That custom navigation service specification was provided by an interface, INavService, and there is a property of that interface type in the BaseViewModel so that a concrete implementation of the service can be provided to the ViewModels as needed.

The benefit of using an interface to define platform-specific services is that it can be used in an agnostic way in the ViewModels and the concrete implementations can be provided via dependency injection. Those concrete implementations can be actual services, or even mocked services for unit testing the ViewModels, as we'll see in Chapter 8, Testing.

In addition to navigation, there are a couple of other platform-specific services our TripLog app could use to enrich its data and experience. In this section, we will create a location service that allows us to get specific geo-coordinates from the device. The actual platform-specific implementation of the location service is fairly trivial and there are tons of resources on how to do this in various ways. We will create a basic implementation without going too deep so that we can keep the focus on how we leverage it as a dependency in a Xamarin.Forms architecture.

Similar to the approach taken for the navigation service, we will first start out by creating an interface for the location service and then create the actual platform-specific implementations.

Creating a location service

The first step to allowing our app to take advantage of the device's geo-location capabilities is to provide an interface in the core library that can be used by the ViewModels in a device and platform agnostic manner. When getting geo-location back from a device, each platform could potentially provide coordinates in a platform-specific data structure. However, each structure will ultimately provide two double values representing the coordinate's latitude and longitude values. There are a couple of ways to ensure the results are returned in a platform-agnostic manner, which we need since we are working in a portable class library.

One way is to pass the values back via a callback method. Another approach, which we will employ, is to use a custom object, which we will define in our Models namespace, as shown in the following steps:

  1. Create a new class named GeoCoords in the Models folder of the core TripLog project.
  2. Add two double properties to the GeoCoords class named Latitude and Longitude:
    public class GeoCoords
    {
        public double Latitude { get; set; }
        public double Longitude { get; set; }
    }
  3. Create a new interface named ILocationService in the Services folder of the core TripLog project. The interface should have one async method, which returns a GeoCoords object:
    public interface ILocationService
    {
        Task<GeoCoords> GetGeoCoordinatesAsync();
    }

Using the location service on the new entry page

Now that we have an interface that defines our location service, we can use it in the core project of our TripLog app. The main place we need to capture location in the app is on the new entry page, so coordinates can be attached to log entries when they are added. Because we want to keep our app logic separated from the user interface, we will use the location service in the new entry page's ViewModel, and not on the Page itself.

In order to use the ILocationService in the NewEntryViewModel, perform the following steps:

  1. First, add a readonly property to the NewEntryViewModel to hold an instance of the location service:
    public class NewEntryViewModel : BaseViewModel
    {
        readonly ILocationService _locService;
        // ...
    }
  2. Next, update the NewEntryViewModel constructor to take an ILocationService instance and set its readonly ILocationService property:
    public NewEntryViewModel (INavService navService, ILocationService locService)
        : base (navService)
    {
        _locService = locService;
    
        Date = DateTime.Today;
        Rating = 1;
    }
  3. Finally, update the NewEntryViewModel Init method to use the location service to set the Latitude and Longitude on the Entry property:
    public override async Task Init ()
    {
        var coords = await
            _locService.GetGeoCoordinatesAsync ();
    
        Latitude = coords.Latitude;
        Longitude = coords.Longitude;
    }

As you can see, we can completely work with our location service in the ViewModel even though we haven't actually written the platform-specific implementation. Although, if we run the app, we will get a runtime error, because the implementation won't actually exist, but it's useful to be able to work with the service through abstraction to fully build out and test the ViewModel.

Adding the location service implementation

Now that we have created an interface for our location service and updated the ViewModel to use it, we need to create the concrete platform-specific implementations. For the purposes of this chapter, we will only create the iOS and Android implementations; however, the companion code for this book contains the Windows implementation as well.

In order to tap into the platform's geo-location capabilities, we can leverage a Xamarin Component named Xamarin.Mobile. The Xamarin.Mobile Component provides an easy API to use device-specific utilities such as geo-location, media, and contacts, as shown in the following steps:

  1. First, add the Xamarin.Mobile Component to each of the platform projects (that is, TripLog.iOS and TripLog.Droid) by right-clicking on the Components folder.
  2. Next, create a new folder in the TripLog.iOS project named Services, and within it, create a new class named LocationService that implements the ILocationService interface we created earlier in the chapter:
    public class LocationService : ILocationService
    {
        public async Task<GeoCoords> GetGeoCoordinatesAsync ()
        {
            var locator = new Geolocator {
                DesiredAccuracy = 30
            };
    
            var position = await
                locator.GetPositionAsync (30000);
    
            var result = new GeoCoords {
                Latitude = position.Latitude,
                Longitude = position.Longitude
            };
    
            return result;
        }
    }
  3. Finally, create a new folder in the TripLog.Droid project named Services, and within it, create a new class named LocationService that implements the ILocationService interface we created earlier in the chapter. When using the Xamarin.Mobile Component to create a location service, the iOS and Android implementations are almost identical. The only major difference between the two implementations is that Android requires the Geolocator to be instantiated with the current Android Activity. In Xamarin.Forms, this can be accessed via the Context property of the static Forms class:
    public class LocationService : ILocationService
    {
        public async Task<GeoCoords> GetGeoCoordinatesAsync ()
        {
            var locator = new Geolocator (Forms.Context) {
               DesiredAccuracy = 30
        };
    
            var position = await
                locator.GetPositionAsync (30000);
    
            var result = new GeoCoords {
                Latitude = position.Latitude,
                Longitude = position.Longitude
            };
    
        return result;
        }
    }

    Note

    On iOS 8.0 and higher, you must enable location requests in the application's Info.plist.

    On Android, you must require ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION in the application's manifest.

Now that we have created a platform-dependent service, it is time to register it into an IoC container so that we can use it throughout the rest of the code. In the next section, we will use Ninject to create registrations between our location service interface and the actual platform-specific implementations. We will also update the custom navigation service that we created in Chapter 3, Navigation Service, to use Ninject in place of the default Xamarin.Forms DependencyService.

..................Content has been hidden....................

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