尝试在 iOS 上使用 Xamarin.Forms 中的 HttpClient 访问 Google Places API 永远挂起

Attempting to access Google Places API with HttpClient in Xamarin.Forms on iOS Hangs Forever

我在公司帐户上有一个 API 密钥,并启用了所有相关 API,包括 Google 地图 API for iOS 、Google 个地点和 Geocoding/Reverse 个地理编码。

API键是正确的,我仔细检查了三遍。

此解决方案在 Android 上完美运行。但是,在 iOS 上,此代码完全失败......甚至设置超时也不起作用,对 GetAsync 的函数调用从不 returns。

这是一个 Xamarin.Forms 项目,这段代码正在主 PCL 中执行。

这是怎么回事?

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading.Tasks;
    using Plugin.Geolocator;
    using System.Net.Http;
    using MyApp.Models;
    using Newtonsoft.Json;
    using MyApp.Localization.Resources;
    using System.Threading;
    using DataTools.Text;
    using Plugin.Geolocator.Abstractions;

    namespace MyApp.Helpers
    {
        public class PlacesHelperConfig
        {
            public Position Position { get; set; }

            public int Pages { get; set; } = 1;

            public bool SuppressErrors { get; set; } = false;

            public string Keyword { get; set; }

            public string PlaceType { get; set; } = "restaurant";

            public double Radius { get; set; } = 1000;

            public bool RankByDistance { get; set; } = false;


        }

        public class PlacesHelper
        {
            private CancellationTokenSource cts = new CancellationTokenSource();




            public void Cancel()
            {
                cts.Cancel();
            }

            public async Task<List<MapPlace>> GetNearbyPlacesAsync(PlacesHelperConfig config)
            {
                return await GetNearbyPlacesAsync(config.Keyword, config.PlaceType, config.Position, config.Radius, config.RankByDistance, config.Pages, config.SuppressErrors);
            }
            public async Task<List<MapPlace>> GetNearbyPlacesAsync(string keyword, string placetype, Position position, double radius, bool byDistance, int pages, bool suppressErrors)
            {
                var places = new List<MapPlace>();
                var url = "https://maps.googleapis.com/maps/api/place/nearbysearch/json";

                string useUrl = null;
                string sJson = null;

                try
                {

                    if (!Plugin.Connectivity.CrossConnectivity.Current.IsConnected)
                    {
                        if (!suppressErrors) Acr.UserDialogs.UserDialogs.Instance.Alert(AppResources.ErrorNoInternet, "", AppResources.OK);
                        return null;
                    }

                    //var locator = CrossGeolocator.Current;
                    HttpResponseMessage resp;

                    if (position == null)
                    {
                        throw new ArgumentNullException("Must provide a position");
                    }

                    var cli = new HttpClient();

                    string reqStr = "";

                    if (!string.IsNullOrEmpty(keyword))
                    {
                        reqStr += "keyword=" + TextTools.UrlEncode(keyword);
                    }

                    if (!string.IsNullOrEmpty(placetype))
                    {
                        if (reqStr != "") reqStr += "&";
                        reqStr += "type=" + TextTools.UrlEncode(placetype);
                    }

                    if (reqStr == "") throw new ArgumentNullException("Must have at least a type or a keyword");

                    if (byDistance) {
                        reqStr += "&fields=photos,formatted_address,name,opening_hours,rating&location={0},{1}&key={2}&rankby=distance";
                        reqStr = string.Format(reqStr, position.Latitude, position.Longitude, ((App)App.Current).MapsApiKey);

                    }
                    else
                    {
                        reqStr += "&fields=photos,formatted_address,name,opening_hours,rating&radius={0}&location={1},{2}&key={3}";
                        reqStr = string.Format(reqStr, radius, position.Latitude, position.Longitude, ((App)App.Current).MapsApiKey);
                    }


                    MapPlaceQueryReturn returned;

                    url += "?" + reqStr;
                    useUrl = url;

                    var uri = new Uri(useUrl);
                    cli.Timeout = new TimeSpan(0, 0, 10);

                    for (int i = 0; i < pages; i++)
                    {
                        try
                        {
                            resp = await cli.GetAsync(uri, cts.Token);
                        }
                        catch
                        {
                            if (!suppressErrors) Acr.UserDialogs.UserDialogs.Instance.Alert(AppResources.ErrorNoServer, "", AppResources.OK);

                            if (places.Count > 0) return places;
                            else return null;
                        }

                        sJson = await resp.Content.ReadAsStringAsync();
                        returned = JsonConvert.DeserializeObject<MapPlaceQueryReturn>(sJson);

                        places.AddRange(returned.Results);

                        if (returned.NextPageToken == null)
                            break;

                        else if (pages > 1)
                            useUrl = url + "&pagetoken=" + returned.NextPageToken;

                    }

                    return places;
                }
                catch
                {
                    if (places.Count > 0) return places;
                    else return null;
                }
            }

        }

   public class MapPlaceQueryReturn
    {

        [JsonProperty("html_attributions")]
        public string[] HtmlAttributions { get; set; }


        [JsonProperty("next_page_token")]
        public string NextPageToken { get; set; }


        [JsonProperty("results")]
        public List<MapPlace> Results { get; set; }


    }

    public class MapPlace
    {

        [JsonProperty("place_id")]
        public string PlaceId { get; set; }

        [JsonProperty("geometry")]
        public PlaceGeometry Geometry { get; set; }

        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("vicinity")]
        public string Address { get; set; }

        [JsonProperty("types")]
        public string[] Types { get; set; }

        [JsonProperty("price_level")]
        public double PriceLevel { get; set; }

        [JsonProperty("rating")]
        public double Rating { get; set; }

        public override string ToString() => $"{Name},{Address}";

        public override bool Equals(object obj) => (obj is MapPlace p && p.PlaceId == PlaceId);

        public override int GetHashCode() => (int)Crc32.Calculate(Encoding.UTF8.GetBytes(ToString()));

    }

    public class PlaceGeometry
    {
        [JsonProperty("location")]
        public PlaceLocation Location { get; set; }

        [JsonProperty("viewport")]
        public PlaceViewport Viewport { get; set; }

        public override string ToString()
        {
            return $"{Location.Latitude},{Location.Longitude}";
        }
    }

    public class PlaceLocation
    {

        [JsonProperty("lat")]
        public double Latitude { get; set; }

        [JsonProperty("lng")]
        public double Longitude { get; set; }

        public override string ToString()
        {
            return $"{Latitude},{Longitude}";
        }

        public PlaceLocation() { }

        public PlaceLocation(double latitude, double longitude)
        {
            Latitude = latitude;
            Longitude = longitude;
        }

        public static implicit operator PlaceLocation(Xamarin.Forms.Maps.Position value)
        {
            return new PlaceLocation(value.Latitude, value.Longitude);
        }

        public static implicit operator Xamarin.Forms.Maps.Position(PlaceLocation value)
        {
            return new Xamarin.Forms.Maps.Position(value.Latitude, value.Longitude);
        }

    }

    public class PlaceViewport
    {

        [JsonProperty("northeast")]
        public PlaceLocation Northeast { get; set; }

        [JsonProperty("southwest")]
        public PlaceLocation Southwest { get; set; }

    }

}

我修好了。发生的事情是,虽然此代码在 PCL 代码中,但调用它的代码在特定于平台的代码中。我创建了一些接口并触发了一些事件,这些事件将 PCL 代码转换为 运行 此代码,并且它起作用了。

我不知道它为什么起作用...但它起作用了。