Wednesday, January 2, 2019

3rd day with Xamarin

The second day with Xamarin was spent setting up the permissions and Azure build Pipeline to build the Android and iOS projects.

Today I am planning to do
  • MVVM in the app 
  • Setting up Dependency Injection 
  • Dialog Service

MVVM


MVVM is Model-View-ViewModel and you can read about it on Microsoft website Here. It is an application pattern that allows us to build de-coupled components. I would suggest reading through the article to get a better understanding of  MVVM components before reading ahead.

I currently have only one view in the app that loads when I start the app and shows maps and the current location of the device. 

Now I want to create a ViewModel for this view and which will get the current location of the device using a location service. I want to create a location service because I might need to get access to the location in other view models as well.

The locationservice class is going to check if it can provide the location if yes, it will return the current location of the device. I have used Permissions.Plugin to check for permissions and show the permission dialog to the user, I am going to use another plugin GeoLocationPlugin.

Here is the code for LocationService
/// This service will provide the current location of the device. 
/// Also checks for required permissions also.
/// 
public class LocationService : ILocationService
{
    public async Task GetCurrentLocation()
    {
        var position = await CrossGeolocator.Current.GetPositionAsync();

        return new LocationInfo { Latitude = position.Latitude, 
                                      Longitude = position.Longitude };
    }

    public bool IsLocationAvailable()
    {
        if (!CrossGeolocator.IsSupported)
        {
            return false;
        }
        return CrossGeolocator.Current.IsGeolocationAvailable;
    }
}

Now I want to make this service available to my ViewModel. The simple way is to create an instance of service in the class and use it. However, what if you want to unit test your ViewModel? you would not have access to the actual device while running your tests so you want to mock the service to provide a mock location. This is one of the reason we would want to use DI container to provide the dependencies needed for ViewModel. Have a read here to familiarize yourself with it.

Dependency Injection


I am planning to use Autofac as Ioc container. Installed the nuget package for Autofac in the Core project. Oh yes! When I say core project means the .Net Standard library project that contains the common code and will refer the specific target projects as Android Project and iOS project, which are obvious :).

Alright, once it is installed we need to configure the container somewhere in the application. Usually, you want to setup it in the application bootstrap. I am going to refer to the implementation of the ViewModelLocator class in the eShopOnContainers app by Microsoft on Github.

I am going to replace tinyIoc container with Autofac. So what is this class doing? If you go through the class what it is doing is creating an attached property to get the currently bounded view and create a viewmodel object for that view and assign it as BindingContext. You would have observed one thing that it uses the convention i.e. name your views and viewmodel the same with different suffix and are in the same assembly. You can tweak the code as per your requirement. I am using it as it.

Wait! I did not mention why I am using a ViewModelLocator class, so we need to provide Views with their corresponding viewmodels as BindingContext. We can do it in few ways e.g. creating a viewmodel object in the view constructor and assign it as BindingContext. However, that way I would not resolve the viewmodel which I do not want to do for each View.

Here is how to use the ViewModelLocator in the view.
<ContentPage
    x:Class="WakeMeUp.Views.MainPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:customMap="clr-namespace:WakeMeUp.CustomControls"
    xmlns:viewModelBase="clr-namespace:WakeMeUp.ViewModels.Base"
    viewModelBase:ViewModelLocator.AutoWireViewModel="True">

Dialog Service


Dialogs are an integral part of the application. Android does give us the ability to show display alert using Page.DisplayAlert but we can't use that in Viewmodel, right? So what do we need to do. We need another service which will show an alert to the user. Again I do not care about the implementation from viewmodel. What I care about is that I need to show are display alert and get a response back if needed.

There is a great lib Acr.Dialogs. The good thing about this is that we can register this in our dependency Ioc container and then use it as a dependency in our viewmodels.

 private async Task WhereAmIAsked()
 {
   if (_locationService.IsLocationAvailable())
   {
       CurrentPosition = await _locationService.GetCurrentLocation();
   }
   else
   {
       await _userDialogs.AlertAsync("Location is not enabled on the device.", 
                                     "System Message", "Ok");
   }
 }

You must be thinking this guy is just showing code and talking about generic stuff that we can find on the internet.

My goal here is to write as I code and what I am trying to do and why I am doing it. So we have written some code. Here is the view of the app.


Note: That's not my location.. Lol...

That location is the location set in the simulator settings. You can change that in the settings and click the button again to see the changes.

So, what I am trying to do is to get a simple app doing something very small and get the infrastructure set up around it.

Now that we have a simple app that loads the map and show the current location of the device on Android simulator. :) We need to see if it works with Mac as well or not.

I have a Mac setup with visual studio installed on it and also xCode. I will cover how to setup a Mac for Xamarin builds later in a separate post.

We need to add permissions to the iOS project. Below is the list of Permissions we need to add to info.plist in the iOS project.


  1. Calendar and Bluetooth permissions are needed for Permissions.Plugin
  2. Location Permissions
    1. LocationAlwaysUsageDescription
    2. LocationWhenInUseDescription
    3. LocationUsageDescription

If you have set the permissions right you should be able to load the application and get the current location of the device.

Location Permission for iOS

After allowing the location permission you should be able to see the current location of the device after clicking the "Where am I" button.

No comments:

Post a Comment