Tag: #XAML

Xamarin Forms – Data retrieval with a network service call

I am currently working through refactoring my native Android application; with that I realized I had never posted about networking using Xamarin using an ObservableCollection. I wanted to see how an actual active network call worked vs. my prior example working with Xamarin ObservableCollection lists using data created by the user in Xamarin Forms – Group ListView using an ObservableCollection. I updated first solution in there to use Forms 5 and to use the current android API.

In the network call, I wanted it to use a REST endpoint, as most endpoints have converted over to it and I wanted to use a endpoint that didn’t require a API key to keep it simpler for everyone. This turned out to be more difficult than I thought it would be. I found two: pokeapi.co, which is all about all Pokemon and openbrewerydb.org that dealt with public information on breweries, cideries, and brewpubs. I ended up using openbrewerydb.org, as that didn’t require multiple calls to piece enough information together to display.

Note: With iOS, you’ll have to either have an Apple developer account or use this method to sign the iOS application, so the app network call will work – otherwise it will not work and throw an error.

Service mapping

I used Postman to explore the JSON results of this call: https://api.openbrewerydb.org/breweries?by_state=ohio&page=5. I ended up choosing Ohio as it had more than 4 pages worth of data. I used https://json2csharp.com and pasted the results into it and it generated out a class:

    public class Root
    {
        public string id { get; set; }
        public string name { get; set; }
        public string brewery_type { get; set; }
        public string street { get; set; }
        public object address_2 { get; set; }
        public object address_3 { get; set; }
        public string city { get; set; }
        public string state { get; set; }
        public object county_province { get; set; }
        public string postal_code { get; set; }
        public string country { get; set; }
        public string longitude { get; set; }
        public string latitude { get; set; }
        public string phone { get; set; }
        public string website_url { get; set; }
        public DateTime updated_at { get; set; }
        public DateTime created_at { get; set; }
    }

The website didn’t quite decode the JSON properly, as it should have made a list instead of a single element; also the object items should have been strings instead – but most of the data for these were null, so that’s OK. To be fair, it created the class w/o any input from me and I was able to fix it easily enough. I ended up calling this class Brewery and put it into the model directory.

Service Call

Next was time to do the service. I’ve seen this example, but ended up using this blog entry as my primary network example. I used the above open brewery link as my URI base and formatted the data parameters into the URI. Once that is done, access the network and then process the result, if it worked OK. If this were a production app, I’d do a few retries, in case the result wasn’t successful.

        public const string BASE_BREWERY_LIST_URL = "https://api.openbrewerydb.org/breweries?by_state={0}&page={1}";

        public static async Task<IList<Brewery>> GetBreweriesAsync(string state, int page)
        {
            IList<Brewery> breweries = new List<Brewery>();

            string url = String.Format(BASE_BREWERY_LIST_URL, state, page);
            using (HttpClient httpClient = new HttpClient())
            {
                System.Diagnostics.Debug.WriteLine("Get data - starting network call");
                var response = await httpClient.GetAsync(url);
                System.Diagnostics.Debug.WriteLine("Get data - network call complete");

                if (response.IsSuccessStatusCode)
                {
                    var content = await response.Content.ReadAsStringAsync();
                    var posts = JsonConvert.DeserializeObject<IList<Brewery>>(content);
                    breweries = posts;
                }
                else
                {
                    httpClient.CancelPendingRequests();
                }
            }

            return breweries;
        }

XAML

Next was to deal with the XAML. As I noted above, I updated my Github fork of the Xamarin forms sample project that used ObservableCollection. In this project in the XAML, there were warning messages complaining about items were not bound. Since they worked fine, it was bound correctly, it was just done via code behind binding here:

BindingContext = new ViewModelSection(Navigation);

I ended up commenting this binding out and moving the binding into to the XAML by adding the viewModel label:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="LabelledSections.LabelledSectionXaml"
             xmlns:viewModel="clr-namespace:LabelledSectionsList;assembly=LabelledSectionsList">

and adding this chunk (as this is a ContentPage), which :

    <ContentPage.BindingContext>
        <viewModel:ViewModelSection />
    </ContentPage.BindingContext>

This required a slight change to the view model to add a constructor that didn’t have any parameters.

Strangely, this took me adding and removing the text ViewModelSection probably 3-4 times as well as cleaning / rebuilding each time and closing / reopening VS Mac a few times before the XAML stopped showing the view model name (ViewModelSection) as not an error. This is one reason to leave the code behind binding 🤨

Jumping back to the network project I wanted to have a top block that dealt with getting the data and below it a list of the data that was brought back. I specifically wanted a count of the number of rows currently in the ObservableCollection brewery list. I originally had this as a separate count in the view model, but changed it to use the actual count of the brewery list for accuracy. I again performed the XAML binding, similar to the other project noted above. As I was not doing an update, I wanted to be sure to set the binding as one way on the list. The caching strategy was from the default construction of the ContentPage, so after looking at the docs, it seemed to be the best of the given strategies w/o moving to another renderer.

            <ListView x:Name="MyListView" 
                    ItemsSource="{Binding LocationList, Mode=OneWay}"
                    CachingStrategy="RecycleElement">

Interestingly enough, the use of Forms 5 allowed me to use XAML hot reload – I had not used it much before. Also interesting is that while running it via VS, both under iOS and Android, it ran pretty janky; but run just on it’s own, it ran fine. I thought that this might be an issue with the thread locking ObservableCollection when adding items to the list, but even after creating a subclass of the collection, it was still janky. I ended up removing the NotifyPropertyChange as ObservableCollection has this built in; but the jank continued. Given this I wonder if there’s a problem with XAML hot reload to cause this – just not sure. Given the advice to use ObservableCollection instead of a normal list, I went ahead and stuck with that, but in this particular case, it might be smarter to move to a list for a possible performance increase as it’s not a two way binding and we aren’t checking for network updates to items in our list.

UI – iOS / Android

I’m glad to have finally done this for comparison. To be sure, it’s much easier than dealing with native Android network calls, but less customizable.

If you want to check out this Xamarin network project, head over to the Github repo.