Xamarin essentials geolocation is not working, GetLocationAsync breaks out of try

Sjoerd Pottuit picture Sjoerd Pottuit · Oct 17, 2018 · Viewed 9.2k times · Source

So I want to get the users location and reverse geocode it to get the address plus street number.

Therefore I installed the package Xamarin.Essentials which is in pre-release. I wanted to use the geolocation and geocoding API so I wrote some code, but the code never reaches the if (location != null) in the try of GetCurrentLocation.

I don't think it matters that Xamarin.Essentials is in pre-release based on what Xam.Plugin.Geolocator author James Montemagno said:

I will continue to work and maintain my Plugins, but I do recommend you checkout Xamarin.Essentials to see if it is a great fit your app as it has been for all of mine!

Source: https://github.com/jamesmontemagno/GeolocatorPlugin

What have I done?

I have searched for StackOverflow questions about the problem I am having, but I can't find one that can help me as far as I know.

The questions I have looked at:

I have also tried if adding a timeout would work: var request = new GeolocationRequest(GeolocationAccuracy.Medium, timeout: TimeSpan.MaxValue );

But I got the same problem on the location just as fast. It never reached the if statement.

My code:

FillInLocationInForm(); is set in my Page constructor.

public Location location; and public Placemark placemark; are set in the Page class.

These are the relevant functions in the Page class:

public async void FillInLocationInForm()
    {
        if (await GetCurrentLocation())
        {
            if (await ReverseGeocodeLocation())
            {
                await this.DisplayAlert("Location", placemark.AdminArea + " " + placemark.FeatureName + " " + placemark.Locality + " " + placemark.SubLocality + " " + placemark.SubAdminArea + " " + placemark.SubThoroughfare + " " + placemark.Thoroughfare, "Okay", "Okay");
            }
        }           
    }

public async Task<bool> GetCurrentLocation ()
    {
        try
        {
            var request = new GeolocationRequest(GeolocationAccuracy.Medium);
            var location = await Geolocation.GetLocationAsync(request);

            if (location != null)
            {                    
                this.location = location;
                return await Task.FromResult(true);
            }
        }
        catch (FeatureNotSupportedException fnsEx)
        {
            // Handle not supported on device exception               
        }
        catch (PermissionException pEx)
        {
            // Handle permission exception                
        }
        catch (Exception ex)
        {
            // Unable to get location              
        }
        return await Task.FromResult(false);
    }

public async Task<bool> ReverseGeocodeLocation()
    {
        try
        {
            var lat = location.Latitude;
            var lon = location.Longitude;

            var placemarks = await Geocoding.GetPlacemarksAsync(lat, lon);

            var placemark = placemarks?.FirstOrDefault();
            if (placemark != null)
            {
                //var geocodeAddress =
                //    $"AdminArea:       {placemark.AdminArea}\n" +
                //    $"CountryCode:     {placemark.CountryCode}\n" +
                //    $"CountryName:     {placemark.CountryName}\n" +
                //    $"FeatureName:     {placemark.FeatureName}\n" +
                //    $"Locality:        {placemark.Locality}\n" +
                //    $"PostalCode:      {placemark.PostalCode}\n" +
                //    $"SubAdminArea:    {placemark.SubAdminArea}\n" +
                //    $"SubLocality:     {placemark.SubLocality}\n" +
                //    $"SubThoroughfare: {placemark.SubThoroughfare}\n" +
                //    $"Thoroughfare:    {placemark.Thoroughfare}\n";

                //Console.WriteLine(geocodeAddress);
                this.placemark = placemark;
                return await Task.FromResult(true);
            }
        }
        catch (FeatureNotSupportedException fnsEx)
        {
            // Feature not supported on device
        }
        catch (Exception ex)
        {
            // Handle exception that may have occurred in geocoding
        }
        return await Task.FromResult(false);
    }

I also added these permissions to my apps AndroidManifest and request them on runtime. I have seen Location and Storage both selected in my app settings.:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" />

The documentation I am following:

Any help is appreciated.

Edit:

I have a hunch I need to add a cancellation token to the GetLocationAsync, but I don't know how yet. I don't see it next to GeolocationRequest:

enter image description here

Edit 2:

I added the CancellationToken, but the if statement is still not reached. Maybe I am not using it right yet.

using System.Threading; // add above

CancellationTokenSource source = new CancellationTokenSource();
CancellationToken cancelToken = source.Token;
var location = await Geolocation.GetLocationAsync(request, cancelToken);

Edit 3:

The breakpoint in the image below is never fired which means the if statement is never reached. Somehow the code goes out of the try but doesn't go in one of the catch statements. I use a Console.WriteLine() in my catch statements now. I see nothing in my device log. I tested a Console.WriteLine() out of the catch which I saw in the device log. enter image description here

Answer

Sjoerd Pottuit picture Sjoerd Pottuit · Oct 22, 2018

Apparently, I didn't wait long enough. Just like Jason said.

My first attempt on adding a timeout was:

new GeolocationRequest(GeolocationAccuracy.Medium, timeout: TimeSpan.MaxValue);

Somehow the line above didn't work.

Anyway adding a timespan of 10 seconds gave me a location:

var request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(10));