Geolocation In Mobile Apps Using Xamarin

Geolocation In Mobile Apps Using Xamarin

A- Geolocation Implementation In Xamarin:

Implementing geolocation requires taking advantage of device-specific location services which help provide a location to a particular user at any given point in time. There are many use cases where geolocation in mobile applications can be implemented such as displaying weather data for a user’s location, finding restaurants nearby and navigating user for a destination.

Even if you are not developing any weather forecasting or mapping application there are many useful application of location services which includes location-specific features or pre-filling data entry forms for improving conversion rates.

B- How to implement Geolocation in Xamarin.Forms

Access to geolocation services from a portable class library or shared project has been once a very tough task. As all mobile operating system iOS, Android and Windows implement location services very differently, therefore, dependency injection or conditional compilation would be quite. Also, each of the providers implement it very different and with different level of complexity.
Plugins for Xamarin are libraries which expose a single set of APIs in order to access common device functionality across the platform iOS, Android, and Windows. This enhances the amount of code which can be shared across multiple platforms hence speeding and simplifying mobile development. Plugins like Geolocator for Xamarin and Windows are amongst the plugins available for Xamarin and Windows. Let’s learn few plugins which can help in incorporating geolocation in your application.

Don’t forget to check: Xamarin Forms And Entity Framework Core, and
Development Of Native UI Mobile Applications Using Xamarin Forms

B-1 Platform Setup

To your portable class library and to the platform-specific projects, add the Geolocator Plugin for Xamarin and Windows NuGet to your cross-platform portable class library. As the user’s location is private we have to configure the application so that it can ask the user if it is okay to access their current location.

B1.1 Android

For allowing an application to access location services we have to enable two Android permissions: ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION. For adding NuGet to the Android project directly, this should be enabled default by you. Make sure the following permissions are enabled:
null
If you are targeting Android Marshmallow (Android M) or above, users will also be prompted automatically for runtime permissions.

B1.2 iOS

Depending on whether you will be using geolocation or just at certain points in a user’s workflow, you either will need to add the key NSLocationWhenInUsageDescription or NSLocationAlwaysUsageDescription in your Info.plist. Along with this a new string entry for the key is required which describes exactly what you will be doing with the user’s location. For supporting background updates (iOS9+ only) you must also enable the AllowsBackgroundUpdatesproperty of the Geolocator. The uibackgroundmodes key with the location value is also required for background updates.

Check this out: Xamarin And iOS10 App Development – What’s New

B1.3 Windows

ID_CAP_LOCATION permission must be enabled.

B1.4 Getting current location using to use it in a Xamarin.Forms.Maps

After browsing almost all the threads about Xamarin.Forms.Labs and Geolocator to get the current location and to use it in a Xamarin.Forms.Maps, I’m about to give up. And I’m quite lost when I see the Geolocation sample of Xamarin.Forms.Labs. As you may guess, I’m a newbie in Xamarin, or so to give you the most handy and easiest solutions.
Install Xamarin.Forms.Labs 1.2.1-pre2 in the PCL, iOS and Android projects via NuGet.

public async Task<Xamarin.Forms.Labs.Services.Geolocation.Position> GetCurrentPosition() {
IGeolocator geolocator = DependencyService.Get();<br/>
 Xamarin.Forms.Labs.Services.Geolocation.Position result = null;

        if (geolocator.IsGeolocationEnabled) {
            try {
                if (!geolocator.IsListening)
                    geolocator.StartListening(1000, 1000);

                var task = await geolocator.GetPositionAsync(10000);

                result = task;

                System.Diagnostics.Debug.WriteLine("[GetPosition] Lat. : {0} / Long. : {1}", result.Latitude.ToString("N4"), result.Longitude.ToString("N4"));
            }
            catch (Exception e) {
                System.Diagnostics.Debug.WriteLine ("Error : {0}", e);
            }
        }

        return result;
    }`
locator.PositionChanged += (sender, e) => {
    var position = e.Position;
    latitudeLabel.Text = position.Latitude;
    longitudeLabel.Text = position.Longitude;
};

C- How to implement Geolocation in Xamarin.Android

C1: Location Services

Android offers access to various location technologies such as cell tower location, GPS, and Wi-Fi. The information of each location technology is fetched through location providers, therefore allowing applications to get the location irrespective of the provider used.
Let us here discuss the android Location Service API, and see how to communicate with the system location service a LocationManager. We will also explore the Google Location Services API using Fused Location Provider from Google Play Services which change location providers dynamically.

C2: Maps

Mobile devices easily complement mapping technologies which are an advantage over laptops and desktops as they do not have in-built location
awareness. Mobile devices use such location awareness applications for locating devices and for displaying a change in location information. Android holds a powerful in-built technology which displays location data on maps using location hardware which may be available on the device. Let us have a look what mapping applications under Xamarin.Android have to offer:

  • In-built mapping functionality to quickly use in map application.
  • Maps API to control a map’s display.
  • A variety of techniques to add graphical overlays.

C3: How to get the location of a device using Android.Location.LocationManager

Let us know how to get the location of the device. Whenever a button on the activity is clicked by the user, the street address which is close the location will be displayed.
null

Note: Android applications should use the Fused Location Provider which is available through Google Play Services.

Steps Involved:

  • First create a new Xamarin.Android application and name it as GetLocation.
  • Now edit AssemblyInfo.cs, and declare the permissions necessary to use the LocationServices:
    [assembly: UsesPermission(Manifest.Permission.AccessFineLocation)]
    [assembly: UsesPermission(Manifest.Permission.AccessCoarseLocation)]
    
  • Declare the permissions necessary to use the Geocoder class. Though it is not very much necessary for getting GPS coordinates of the device, but it is an attempt to give street address for current location:
     [assembly: UsesPermission(Manifest.Permission.Internet)] 
  • Edit Main.axml so it contains two TextViews and a Button:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:paddingTop="20dp"
        android:paddingLeft="8dp"
        android:paddingRight="8dp">
        <TextView
            android:layout_width="fill_parent"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:layout_height="wrap_content"
            android:text="Location (when available)"
            android:id="@+id/location_text"
            android:layout_marginBottom="15dp" />
        <Button
            android:layout_width="fill_parent"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:layout_height="wrap_content"
            android:id="@+id/get_address_button"
            android:text="Get Address" />
        <TextView
            android:layout_width="fill_parent"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:layout_height="wrap_content"
            android:text="Address (when available)"
            android:id="@+id/address_text"
            android:layout_marginTop="10dp" />
    </LinearLayout>
    
    
  • Add some instance variables to Activity1.cs:
    static readonly string TAG = "X:" + typeof (Activity1).Name;
    TextView _addressText;
    Location _currentLocation;
    LocationManager _locationManager;
    
    string _locationProvider;
    TextView _locationText;
    
  • Change OnCreate:
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);
        SetContentView(Resource.Layout.Main);
    
        _addressText = FindViewById<TextView>(Resource.Id.address_text);
        _locationText = FindViewById<TextView>(Resource.Id.location_text);
        FindViewById<TextView>(Resource.Id.get_address_button).Click += AddressButton_OnClick;
    
        InitializeLocationManager();
    }
    

    Handler for button click will be covered below. The logic for initializing the LocationManager is placed in its own method for clarity.

  • Add a method called InitializeLocationManager to Activity1:
    void InitializeLocationManager()
    {
        _locationManager = (LocationManager) GetSystemService(LocationService);
        Criteria criteriaForLocationService = new Criteria
                                              {
                                                  Accuracy = Accuracy.Fine
                                              };
        IList<string> acceptableLocationProviders = _locationManager.GetProviders(criteriaForLocationService, true);
    
        if (acceptableLocationProviders.Any())
        {
            _locationProvider = acceptableLocationProviders.First();
        }
        else
        {
            _locationProvider = string.Empty;
        }
        Log.Debug(TAG, "Using " + _locationProvider + ".");
    }
    

    The LocationManager class will listen for GPS updates from the device and notify the application by way of events. In this example we ask Android for the best location provider that matches a given set of Criteria and provide that provider to LocationManager.

  • Edit Activity1.cs and have it implement the interface ILocationListener and add in the methods required by that interface:
    [Activity(Label = "Get Location", MainLauncher = true, Icon = "@drawable/icon")]
    public class Activity1 : Activity, ILocationListener
    {
        // removed code for clarity
    
        public void OnLocationChanged(Location location) {}
    
        public void OnProviderDisabled(string provider) {}
    
        public void OnProviderEnabled(string provider) {}
    
        public void OnStatusChanged(string provider, Availability status, Bundle extras) {}
    }
    
  • Override OnResume so that Activity1 will begin listening to the LocationManager when the activity comes into the foreground:
    protected override void OnResume()
    {
        base.OnResume();
        _locationManager.RequestLocationUpdates(_locationProvider, 0, 0, this);
    }
    
  • Override OnPause and unsubscribe Activity1 from the LocationManager when the activity goes into the background:
     
    protected override void OnPause()
    {
        base.OnPause();
        _locationManager.RemoveUpdates(this);
    }
    

    Note: By unsubscribing from the LocationManager we help in reducing the demands on the battery whenever the activity goes into the background.

  • Add an event handler called AddressButton_OnClick to Activity1. This button allows the user to try and get the address from the latitude and longitude. The snippet below contains the code for AddressButton_OnClick:
    async void AddressButton_OnClick(object sender, EventArgs eventArgs)
    {
        if (_currentLocation == null)
        {
            _addressText.Text = "Can't determine the current address. Try again in a few minutes.";
            return;
        }
        Address address = await ReverseGeocodeCurrentLocation();
        DisplayAddress(address);
    }
    async Task
    ReverseGeocodeCurrentLocation() { Geocoder geocoder = new Geocoder(this); IList
    addressList = await geocoder.GetFromLocationAsync(_currentLocation.Latitude, _currentLocation.Longitude, 10); Address address = addressList.FirstOrDefault(); return address; } void DisplayAddress(Address address) { if (address != null) { StringBuilder deviceAddress = new StringBuilder(); for (int i = 0; i < address.MaxAddressLineIndex; i++) { deviceAddress.AppendLine(address.GetAddressLine(i)); } // Remove the last comma from the end of the address. _addressText.Text = deviceAddress.ToString(); } else { _addressText.Text = "Unable to determine the address. Try again in a few minutes."; } }

    The ReverseGeocodeCurrentLocation method will asynchronously lookup a collection of Address objects for the current location. There are many factors such as location and network availability, none, one or multiple addresses which will be returned. The first address will be passed to the method DisplayAddress which will display the address in the Activity.

  • Update the method OnLocationChanged to display the latitude and longitude when GPS updates are received and update the address:

    public async void OnLocationChanged(Location location)
    {
        _currentLocation = location;
        if (_currentLocation == null)
        {
            _locationText.Text = "Unable to determine your location. Try again in a short while.";
        }
        else
        {
            _locationText.Text = string.Format("{0:f6},{1:f6}", _currentLocation.Latitude, _currentLocation.Longitude);
            Address address = await ReverseGeocodeCurrentLocation();
            DisplayAddress(address);
        }
    }
    

    Add the System.Xml.Linq assembly, which can be done by Right clicking on Resources > Edit Resources and selecting the correct assembly.

  • Now run the application. In a short while the location of the GPS gets be displayed. It may take a little while to display the coordinates:
    Get Address
  • Click the button Get Address, and then the location should be translated into a street address:
    Get Address Code

Note: Additional Information

The LocationManager provides access to the system location services, while the Geocoder class is used to retrieve an address associated with the given latitude and longitude. The Geocoder class makes a network call to Google's server to lookup the address, so because of this the call should be performed asynchronously.

This will also help you a lot: Why Should Enterprises Use Xamarin for App Development?

D- How to implement Geolocation in Xamarin.iOS

Let us build an iOS Location application which prints information about our current location, latitude, longitude, and other parameters to the screen. It will explain how to properly perform location updates while the application is either Active or in Background.

While this example an application we will also see some background concepts which includes registering an application as a background necessary application, suspending UI updates which
the application is in the background and working with the WillEnterBackground and WillEnterForeground AppDelegate methods.

D.1.0. Application set up

1. Create a new iOS > App > Single View Application (C#). Call it Location, and ensure that you have selected both iPad and iPhone.

2. Register the application as a Location application by editing the Info.plist file for our project.

3. Double click on Info.plist file to open it, and scroll to the bottom of the list which is under Solution Explorer. Place a check by both the Enable Background Modes and the Location Updates checkboxes.

In Xamarin Studio, something like this will be seen:
Application setup

Note: In Visual Studio, Info.plist needs to be updated manually by adding the following key/value pair:

  <key>UIBackgroundModes</key>
      <array>
        <string>location</string>
       </array>

Once the application is registered, we need now to get location data from the device to our application. CLLocationManager can help us in doing this in iOS. CLLocationManager offers a key class of Core Location and will raise events giving us location updates.

Let’s create a new class called LocationManager which provides a single place for various screens and code in order to subscribe location updates. In LocationManager class, create an instance of the CLLocationManager called LocMgr:

public class LocationManager
{
  protected CLLocationManager locMgr;
  public LocationManager (){
    this.locMgr = new CLLocationManager();
    this.locMgr.PausesLocationUpdatesAutomatically = false; 
    // iOS 8 has additional permissions requirements
    if (UIDevice.CurrentDevice.CheckSystemVersion (8, 0)) {
      locMgr.RequestAlwaysAuthorization (); // works in background
      //locMgr.RequestWhenInUseAuthorization (); // only in foreground
    }
    if (UIDevice.CurrentDevice.CheckSystemVersion (9, 0)) {
       locMgr.AllowsBackgroundLocationUpdates = true;
    }
  }
  public CLLocationManager LocMgr{
    get { return this.locMgr; }
  }
}

There are some properties and permissions on the CLLocationManager:

  • PausesLocationUpdatesAutomatically – This is a Boolean. It can be set depending on whether the system is allowed to pause location updates. On some device, it defaults to true. This causes the device to stop getting background location updates after about 15 minutes.
  • RequestAlwaysAuthorization - Passing this method give the application user the option to allow the location to be accessed in the background.
  • RequestWhenInUseAuthorization- It is passed if you want to give the user the option to allow the location to be accessed only when the app is in the foreground.
  • AllowsBackgroundLocationUpdates- This Boolean property which is introduced in iOS 9 allow an application to receive location updates when suspended.

Adding a key NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription with a string will display user the alert which requests location data access.

iOS 9 would require while using AllowsBackgroundLocationUpdates the Info.plist includes the key UIBackgroundModes with the value of location. If you have completed the step 2 of this process this will already be present in your Info.plist file.

4. Create a method called StartLocationUpdates with the following code inside the LocationManager class. This will help in setting the update parameters and start receiving location updates from the CLLocationManager:

if (CLLocationManager.LocationServicesEnabled) {
  //set the desired accuracy, in meters
  LocMgr.DesiredAccuracy = 1;
  LocMgr.LocationsUpdated += (object sender, CLLocationsUpdatedEventArgs e) =>
  {
      // fire our custom Location Updated event
      LocationUpdated (this, new LocationUpdatedEventArgs (e.Locations [e.Locations.Length - 1]));
  };
  LocMgr.StartUpdatingLocation();
}

Various other things will also be happening in this method. There are several important things happening in this method. Like first, we will perform a check in order to see if the application has access to location data on the device. This can be verified by calling LocationServicesEnabled on the CLLocationManager. It will return a false value if the user denies the application access to location information.

Secondly, tell the location manager how often to update. CLLocationManager offers many options for filtering and configuring location data.This includes frequency of updates. Here we will set the DesiredAccuracy for updating occurrence of location change by the meter.

At last call StartUpdatingLocation on the CLLocationManager instance. This will tell locationmanager to get an initial fix on the current location and then start sending updates.

5. Till now we have created a location manager, which specifies the kind of data we want to get and have got an initial fix on our location. So now we need a way to get the location data to our View. This can be done with a custom event which takes a CLLocation as an argument:

// event for the location changing
  public event EventHandler<LocationUpdatedEventArgs> LocationUpdated = delegate { };

Here you go for another step to subscribe location updates from the CLLocationManager, and then raise the custom LocationUpdatedevent. This can be done when new location data becomes available for passing in the location as an argument. By doing this a new class LocationUpdateEventArgs.cs gets created. This code is accessible within main application and returns the device location whenever an event is raised:

public class LocationUpdatedEventArgs : EventArgs
{
    CLLocation location;
    public LocationUpdatedEventArgs(CLLocation location)
    {
       this.location = location;
    }
    public CLLocation Location
    {
       get { return location; }
    }
}

User Interface

6. Going further we ready to build the screen which will display our location. Click double on Main.storyboard file for opening them in iOS Designer.

In iOS Designer, you can drag several labels onto the screen which will act as placeholders of location information which we want to display.

So, the layout should resemble the following:
layout resemble

7. Double click the ViewController.cs file and edit it to create a new instance of the LocationManager and call StartLocationUpdateson it, in the Solution Pad.

Alter the code which will look like the one mentioned below:

#region Computed Properties
public static bool UserInterfaceIdiomIsPhone {
            get { return UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone; }
        }
public static LocationManager Manager { get; set;}
#endregion

#region Constructors
public ViewController (IntPtr handle) : base (handle)
{
// As soon as the app is done launching, begin generating location updates in the location manager
    Manager = new LocationManager();
    Manager.StartLocationUpdates();
}
#endregion

This code will start the location updates on the application start-up, though no data will be displayed. This will be addressed in next step.

8. Now that we have location updates, we can begin updating the screen with location data. Below mentioned method will get the location from LocationUpdated event and prints it to the screen:

#region Public Methods
public void HandleLocationChanged (object sender, LocationUpdatedEventArgs e)
{
    // Handle foreground updates
    CLLocation location = e.Location;

    LblAltitude.Text = location.Altitude + " meters";
    LblLongitude.Text = location.Coordinate.Longitude.ToString ();
    LblLatitude.Text = location.Coordinate.Latitude.ToString ();
    LblCourse.Text = location.Course.ToString ();
    LblSpeed.Text = location.Speed.ToString ();

    Console.WriteLine ("foreground updated");
}

#endregion

Subscribe to the LocationUpdated event in AppDelegate, and call the new method to update the UI. This can be done in ViewDidLoad, right after the StartLocationUpdates call:

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();

    // It is better to handle this with notifications, so that the UI updates
    // resume when the application re-enters the foreground!
    Manager.LocationUpdated += HandleLocationChanged;
}

Now, post running it, the application will look like
Geo-location Xamarin Apps

Handling Active and Background states

9. Now is the time to see what happens when the application enters the background. Override the AppDelegatemethods which track application state changes, so that the application is able to notify us when transits between the foreground and the background:

public override void DidEnterBackground (UIApplication application)
{
  Console.WriteLine ("App entering background state.");
}
public override void WillEnterForeground (UIApplication application)
{
  Console.WriteLine ("App will enter foreground");
}

Verify location updates when the app is in the background. Now add the following code in the LocationManager to continuously print updated location data to the application output:

public class LocationManager
{
  public LocationManager ()
  {
    ...
    LocationUpdated += PrintLocation;
  }
  ...

  //This will keep going in the background and the foreground
  public void PrintLocation (object sender, LocationUpdatedEventArgs e) {
    CLLocation location = e.Location;
    Console.WriteLine ("Altitude: " + location.Altitude + " meters");
    Console.WriteLine ("Longitude: " + location.Coordinate.Longitude);
    Console.WriteLine ("Latitude: " + location.Coordinate.Latitude);
    Console.WriteLine ("Course: " + location.Course);
    Console.WriteLine ("Speed: " + location.Speed);
  }
}

10. The application may work pretty well but there can come some issue. iOS will be terminated if there is an attempt to update the UI when the application is supposed to be running in the background. Hence we need to ensure that we have to unsubscribe the location updates and stop updating them if the application goes in the background.

iOS gives us the notifications when the application is about to transit to different application states. We can subscribe to the ObserveDidEnterBackground Notification.

Below is the code snippet which shows how to use a Notification to let the View know when to halt UI updates. This will go in ViewDidLoad:

UIApplication.Notifications.ObserveDidEnterBackground ((sender, args) => {
  Manager.LocationUpdated -= HandleLocationChanged;
});

When app runs, the code will throw the below mentioned output:
Coding

11. When the application is operating in foreground, it will print location updates to the screen and while application operates in background it continues to print data to the application output window.

Another issue remains unattended which is the screen starts UI updates when the app is first loaded, but there is no way of knowing when the application has re-entered the foreground. So if the background application is brought back in foreground the UI updates will not resume.

The solution for this is, that we can nest our call to start UI updates inside another Notification, which will fire when the application enters the Active state:

UIApplication.Notifications.ObserveDidBecomeActive ((sender, args) => {
  Manager.LocationUpdated += HandleLocationChanged;
});

By doing this UI will begin updating when the application is first started. It resumes updating any time the application comes back into the foreground.

Related Post: Which Hyperlocal Idea You Want To Build As A Business?

E- The Last Code

So, here is the implementation of geo-location in Xamarin forms, iOS and Android applications. For more technical proficiency and application development consultancy please feel free to ask for Algoworks Cross Platform Developers tips.

The following two tabs change content below.
Rachit Agarwal

Rachit Agarwal

Director and Co-Founder at Algoworks Technologies
Rachit is leading the mobility business development function, mobility strategy and consulting practice at Algoworks. He is an expert of all mobile technologies and has experience in managing teams involved in the development of custom iPhone/iPad/Android apps.
Rachit Agarwal

Latest posts by Rachit Agarwal (see all)

Rachit AgarwalGeolocation In Mobile Apps Using Xamarin