2

In a Xamarin.Forms app I'm using GeoLocator to retrieve info about my position. The method is async. I have to return the object that contains the position parameters but I can't.

This is the constructor of my class PositionPage

public PositionPage()
{           
    getCurrentPosition();

    var map = new Map(
    MapSpan.FromCenterAndRadius(
        new Position(45.987487, 9.366337), Distance.FromMiles(0.3)))
        {
            IsShowingUser = true,
            HeightRequest = 100,
            WidthRequest = 960,
            VerticalOptions = LayoutOptions.FillAndExpand
        };
    var stack = new StackLayout { Spacing = 0 };
    stack.Children.Add(map);
    Content = stack;            
}

And this is the async method (Now is a void method):

public async void getCurrentPosition() 
{
    try
    {
        var locator = CrossGeolocator.Current;
        locator.DesiredAccuracy = 50;

        var position = await locator.GetPositionAsync(timeoutMilliseconds: 10000);

        Debug.WriteLine("Position Status: {0}", position.Timestamp);
        Debug.WriteLine("Position Latitude: {0}", position.Latitude);
        Debug.WriteLine("Position Longitude: {0}", position.Longitude);                
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Unable to get location, may need to increase timeout: " + ex);
    }
}

I need to return a position object when I call getCurrentPosition() to pass it in the MapSpan.

How can I do that?

1
  • 3
    You should not ever call an asynchronous method from a constructor, I would strongly advise rethinking the design of your code. Commented Mar 14, 2017 at 14:43

4 Answers 4

6

None of the other answers seem to address a major concern with your code so I will add my answer.

You have an async method getCurrentPosition(). This method awaits a call: locator.GetPositionAsync(timeoutMilliseconds: 10000). The only concern with this code is that you are not returning the result you get from locator.GetPositionAsync(). You can fix that by doing this:

//SomeType needs to be the same type that locator.GetPositionAsync() returns
//Also C# naming conventions say that any async methods should end with "Async", 
//obviously not required but it will make your life easier later
public async Task<SomeType> GetCurrentPositionAsync() 
{
    SomeType position = null;
    try
    {
        var locator = CrossGeolocator.Current;
        locator.DesiredAccuracy = 50;

        position = await locator.GetPositionAsync(timeoutMilliseconds: 10000);
        Debug.WriteLine("Position Status: {0}", position.Timestamp);
        Debug.WriteLine("Position Latitude: {0}", position.Latitude);
        Debug.WriteLine("Position Longitude: {0}", position.Longitude);            
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Unable to get location, may need to increase timeout: " + ex);
    }
    return position;
}

The biggest issue you have is that your constructor is attempting to call the async method which will be a synchronous call which basically negates the benefits of async/await.

I would strongly advise changing the design of this application so you are taking advantage of async/await. It wouldn't have to be a major change. You could implement a static async method that constructs an object and returns it for you, that way you can await that call. Something like this:

public class PositionPage
{
    public static async Task<PositionPage> CreateAsync()
    {
        var position = await GetCurrentPositionAsync();

        var map = new Map(
            MapSpan.FromCenterAndRadius(
                new Position(45.452481, 9.166337),
                Distance.FromMiles(0.3)))
        {
            IsShowingUser = true,
            HeightRequest = 100,
            WidthRequest = 960,
            VerticalOptions = LayoutOptions.FillAndExpand
        };
        var stack = new StackLayout { Spacing = 0 };
        stack.Children.Add(map);


        var positionPage= new PositionPage();
        positionPage.Content = stack;
        return positionPage;
    }

    private PositionPage()
    {

    }

    //SomeType needs to be the same type that locator.GetPositionAsync() returns
    //Also C# naming conventions say that any async methods should end with "Async", obviously not required but it will make your life easier later
    public async Task<SomeType> GetCurrentPositionAsync() 
    {
        try
        {
            var locator = CrossGeolocator.Current;
            locator.DesiredAccuracy = 50;

            var position = await locator.GetPositionAsync(timeoutMilliseconds: 10000);
            Debug.WriteLine("Position Status: {0}", position.Timestamp);
            Debug.WriteLine("Position Latitude: {0}", position.Latitude);
            Debug.WriteLine("Position Longitude: {0}", position.Longitude);
        }
        catch (Exception ex)
        {
            Debug.WriteLine("Unable to get location, may need to increase timeout: " + ex);
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

5

You would need to return Task<T> from your async method, do not make it return void, you need to refactor the method like:

public async Task<Geoposition> getCurrentPosition() 
{
  Geoposition position = null;
  try
  {
     var locator = CrossGeolocator.Current;
     locator.DesiredAccuracy = 50;

     position = await locator.GetPositionAsync(timeoutMilliseconds: 10000);

     Debug.WriteLine("Position Status: {0}", position.Timestamp);
     Debug.WriteLine("Position Latitude: {0}", position.Latitude);
     Debug.WriteLine("Position Longitude: {0}", position.Longitude);

  }
  catch (Exception ex)
  {
     Debug.WriteLine("Unable to get location, may need to increase timeout: " + ex);
  }

  return position;

}

and on calling side you would also need to make that method async for ideal scenario, but if you want to call the async method synchronouly then on calling side it would be like:

var position = getCurrentPosition().Result;
// please note that it will be blocking call
// it is synchronously calling the async method

I am not aware what is the return type of GetLocationAsync, but it would be return Task<T> where T would be some specific type, so better would be set the return type of method to Task<Geoposition>, assuming it returns Task<Geoposition>, but you can replace it with whatever is the return type of GetPositionAsync method.

Or alternatively you might want to keep the getCurrentPosition synchrnous which can be done like:

public Geoposition getCurrentPosition() 
{
  Geoposition position = null;
  try
  {
     var locator = CrossGeolocator.Current;
     locator.DesiredAccuracy = 50;

     position = locator.GetPositionAsync(timeoutMilliseconds: 10000).Result;

     Debug.WriteLine("Position Status: {0}", position.Timestamp);
     Debug.WriteLine("Position Latitude: {0}", position.Latitude);
     Debug.WriteLine("Position Longitude: {0}", position.Longitude);

  }
  catch (Exception ex)
  {
     Debug.WriteLine("Unable to get location, may need to increase timeout: " + ex);
  }

  return position;

}

Hope it helps!

1 Comment

OP is calling this method from the controller synchronously, returning Task instead of void will not fix their problem
1

GetPositionAsync return Task<Position>

I think you should use

public async Task<Position> getCurrentPosition() {

        Position position = null;

        try
        {
            var locator = CrossGeolocator.Current;
            locator.DesiredAccuracy = 50;

            position = await locator.GetPositionAsync(timeoutMilliseconds: 10000);

            Debug.WriteLine("Position Status: {0}", position.Timestamp);
            Debug.WriteLine("Position Latitude: {0}", position.Latitude);
            Debug.WriteLine("Position Longitude: {0}", position.Longitude);

        }
        catch (Exception ex)
        {
            Debug.WriteLine("Unable to get location, may need to increase timeout: " + ex);
        }

        return position;
    }

Comments

-2

The GetCurrentPosition right now has a returntype void, so if you want to return a value of a certain type you should define the return type. Because it is also an asynchronous function the returntype for GetCurrentPosition should be Task like this:

public async Task<Position> getCurrentPosition() { 
  var position = await locator.GetPositionAsync(timeoutMilliseconds: 10000);
  return position;
}

Also, the caller function should use have an await like this:

Public PositionPage() {
    Position position = await getCurrentPosition();
}

2 Comments

I tried but it returns only the Task infos like asyncState, id, status, exceptions and many others. Nothing about the object
The reason you are getting that is because you are calling the method from your constructor. You will never get the contents of the Task because you are not running it asynchronously. See my comment on your post.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.