Home > front end >  How do I change a synchronous method to an asynchronous one in CustomMapRenderer - xamarin.forms.map
How do I change a synchronous method to an asynchronous one in CustomMapRenderer - xamarin.forms.map

Time:03-30

I extract data from asynchronous method from API.

public async Task<IEnumerable<AladinModel>> GetDataFromAPI(string indexOnClick)
    {
        DateTime dtNow = DateTime.Now;
        var dtNowAPI = dtNow.ToString("yyyy-MM-dd");

        var listData = new List<AladinModel>();

        var result = await _restServiceAPI.GetAladinData(GenerateRequestUri(ConstantsAPI.EndPoint, dtNowAPI, indexOnClick));

        foreach (var item in result)
        {
            var currentData = new AladinModel()
            {

                Dats = item.Dats,
                Ta = item.Ta,
                Rh = item.Rh,
                Ws = item.Ws,
                Rr = item.Rr,
                Sr = item.Sr,
                Apres = item.Apres

            };
            listData.Add(currentData);
        }

        return listData;
    }

I want to pass the asynchronous method to another synchronous one:

          CustomPin GetCustomPin(Marker annotation)
        {
            string addressIndex = annotation.Id.Substring(1);

            var result = GetDataFromAPI(addressIndex);

            MessagingCenter.Send<object, IEnumerable<AladinModel>>(this, "PinSelected", (IEnumerable<AladinModel>)result);

            var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
            foreach (var pin in customPins)
            {
                if (pin.Position == position)
                {
                    return pin;
                }
            }
            return null;
        }

When I trying to change method to asynchronous like that:

 async CustomPin GetCustomPin(Marker annotation)

I receive error:

CS1983: The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T>

CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Is there a way to make GetCustomPin asynchronous method.

If there is no such option, how to make the class that accepts the request asynchronously, to become synchronous?

    using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using static pizhevsoft.Models.ItemsAPI;

namespace pizhevsoft.Services
{
    public class RestServiceAPI
    {
        HttpClient _client;

        public RestServiceAPI()
        {
            _client = new HttpClient();
        }

        public async Task<List<AladinModel>> GetAladinData(string query)
        {
            List<AladinModel> weatherData = new List<AladinModel>();
            try
            {
                var response = await _client.GetAsync(query);
                if (response.IsSuccessStatusCode)
                {
                    var content = await response.Content.ReadAsStringAsync();
                    JObject root = JObject.Parse(content);
                    weatherData = root["aladinModel"][0].ToObject<List<AladinModel>>();
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("\t\tERROR {0}", ex.Message);
            }

            return weatherData;
        }
    }
}

The example is from here =========UPDATE=========

I trying to convert GetCustomPin to synchronous like that:

async Task<CustomPin> GetCustomPinAsync(Marker annotation)
    {
        string addressIndex = annotation.Id.Substring(1);

        var result = await GetDataFromAPI(addressIndex);

        MessagingCenter.Send<object, IEnumerable<AladinModel>>(this, "PinSelected", (IEnumerable<AladinModel>)result);

        var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
        foreach (var pin in customPins)
        {
            if (pin.Position == position)
            {
                return pin;
            }
        }
        return null;
    }

But now I receive errors in these two methods:

void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
    {
        var customPin = GetCustomPinAsync(e.Marker);
        if (customPin == null)
        {
            throw new Exception("Custom pin not found");
        }

        if (!string.IsNullOrWhiteSpace(customPin.Url))
        {
            var url = Android.Net.Uri.Parse(customPin.Url);
            var intent = new Intent(Intent.ActionView, url);
            intent.AddFlags(ActivityFlags.NewTask);
            Android.App.Application.Context.StartActivity(intent);
        }
    }

    public Android.Views.View GetInfoContents(Marker marker)
    {
        var inflater = Android.App.Application.Context.GetSystemService(Context.LayoutInflaterService) as Android.Views.LayoutInflater;
        if (inflater != null)
        {
            Android.Views.View view;

            var customPin = GetCustomPinAsync(marker);
            if (customPin == null)
            {
                throw new Exception("Custom pin not found");
            }

            if (customPin.Name.Equals("Xamarin"))
            {
                view = inflater.Inflate(Resource.Layout.XamarinMapInfoWindow, null);
            }
            else
            {
                view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
            }

            var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
            var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);

            if (infoTitle != null)
            {
                infoTitle.Text = marker.Title;
            }
            if (infoSubtitle != null)
            {
                infoSubtitle.Text = marker.Snippet;
            }

            return view;
        }
        return null;
    }

Erros:

/Users/pizhev/Projects/pizhevsoft v3/pizhevsoft/pizhevsoft/pizhevsoft.Android/CustomMapRenderer.cs(54,54): Error CS1061: 'Task<CustomPin>' does not contain a definition for 'Url' and no accessible extension method 'Url' accepting a first argument of type 'Task<CustomPin>' could be found (are you missing a using directive or an assembly reference?) (CS1061) (pizhevsoft.Android)

/Users/pizhev/Projects/pizhevsoft v3/pizhevsoft/pizhevsoft/pizhevsoft.Android/CustomMapRenderer.cs(31,31): Error CS1061: 'Task<CustomPin>' does not contain a definition for 'Name' and no accessible extension method 'Name' accepting a first argument of type 'Task<CustomPin>' could be found (are you missing a using directive or an assembly reference?) (CS1061) (pizhevsoft.Android)

CodePudding user response:

your declaration of GetCustomPin is wrong, it should be

async Task<CustomPin> GetCustomPin(Marker annotation)
{
...
}

and you call like that
CustomPin myCustomPin = await GetCustomPin(annotation)

by convention, its better to suffix your async Method with Async

async Task<CustomPin> GetCustomPinAsync(Marker annotation)
{
...
}
CustomPin myCustomPin = await GetCustomPinAsync(annotation)

if you are unable to make GetInfoContents async, maybe you can try that. Im not sure what will happen when you will try to update your view, maybe you will have problem to access to the ui thread.

public Android.Views.View GetInfoContents(Marker marker)
{
     GetInfoContentsAsync(maker);//you will have a warning here
}
 private async Task Android.Views.View GetInfoContentsAsync(Marker marker)
    {
        var inflater = Android.App.Application.Context.GetSystemService(Context.LayoutInflaterService) as Android.Views.LayoutInflater;
        if (inflater != null)
        {
            Android.Views.View view;

            var customPin = await GetCustomPinAsync(marker);
            if (customPin == null)
            {
                throw new Exception("Custom pin not found");
            }

            if (customPin.Name.Equals("Xamarin"))
            {
                view = inflater.Inflate(Resource.Layout.XamarinMapInfoWindow, null);
            }
            else
            {
                view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
            }

            var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
            var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);

            if (infoTitle != null)
            {
                infoTitle.Text = marker.Title;
            }
            if (infoSubtitle != null)
            {
                infoSubtitle.Text = marker.Snippet;
            }

            return view;
        }
        return null;
    }
  • Related