Consuming RESTful Web Service in your Xamarin.Forms Application

by Cedric Gabrang • Aug 10th 2019




Last month, my friend, Mr. Bryan Anthony Garcia, who's a Microsoft MVP for Developer Technologies invited me as one of the speakers of Mobile. NET Developers PH's (MONDPH) 1 whole day Xamarin Workshop to talk about RESTful Web Services + Xamarin.Forms. The topic itself caught the attention of most of the attendees especially when we started creating our app step-by-step. We've created an app which basically consumes data from a web service and for this session we've used AccuWeather Locations API/Web Service. At the end of our topic, we have this simple app which capable of searching different cities around the world and able to display it's current weather condition and temperature.


So on this blog, I'll show how we've consumed data from AccuWeather and used it to create a simple Weather Application using Xamarin.Forms.


Let's get started.



What is RESTful Web Service?


Web service is a standardized way or medium to propagate communication between the client and server applications on the World Wide Web.


REST is a way to access resources which lie in a particular server. For example, you could have a server that could be hosting a database with . All of these are an example of resources. If a client, say an application needs any of these resources, it has to send a request to the server to access these resources. Now REST defines a way on how these resources can be accessed.

Web service has to comply with few characteristics in order for it to be called RESTful. These characteristics below are also known as design principles which need to be followed when working with RESTful based services.

RESTFul Client-Server,  it means that the server will have a RESTful web service which would provide the required functionality to the client. The client send's a request to the web service on the server.

Stateless, this concept means that the client session is stored on the client. The server is stateless means that every server can service any client at any time, there is no session affinity or sticky sessions. The relevant session information is stored on the client and passed to the server as needed.

Cache, refers to storing the server response in the client itself, so that a client need not make a server request for the same resource again and again.

Layered System, where you deploy the services on server A, and store data on server B and authenticate requests in Server C, for example. A client cannot ordinarily tell whether it is connected directly to the end server, or to an intermediary along the way.

Interface/Uniform Contract, RESTful basically works on the HTTP web layer and uses the below key verbs to work with resources on the server:

  • POST - To create a resource on the server
  • GET - To retrieve a resource from the server
  • PUT - To change the state of a resource or to update it
  • DELETE - To remove or delete a resource from the server


Then what is AccuWeather Locations Web Service?


The AccuWeather Locations Web Service  allows developers to get a location key corresponding with a given location. This key can be used to retrieve weather data from the AccuWeather Forecast and Current Conditions APIs. AccuWeather is a company that provides highly accurate real-time weather information for 2.3 million locations around the world.

Here's an example of AccuWeather Locations web service call:


Resource URL

http://dataservice.accuweather.com/locations/v1/cities/search?apikey=J30ZRfw8QYBfrgQWnDJYNOxpV8e8vx1O&q=manila%2C%20ph

Response



Try it yourself!


First, register to https://developer.accuweather.com/apis. Once you already have your account, sign it on then go to My Apps. Click on the Add a new App button then fill in the required details to get your AccuWeather API key. This key is your valid access to use the AccuWeather services.


Add a new App

Now, go to API REFERENCE then select Locations API. Look for the City Search and key in your query parameters.


City Search

Now that you've learned about Web Services and explored AccuWeather Locations API/Web Service, let's go to our main objective.



Creating our app!


First, in our Xamarin.Forms main project, let's create a ViewModel class named WeatherViewModel and inherit the INotifyPropertyChanged interface.


class WeatherViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Then let's initialize two static string variables. This will hold our API Key and our web service city URL.


static string APIKey = "[PLACE YOUR API KEY HERE]";
static string cityURL = "http://dataservice.accuweather.com/locations/v1/cities/";

Now let's have these following variables and setters and getters. This will be later used to process and return web service data.


public string DisplayMessage
{
    get
    {
        if (string.IsNullOrEmpty(Key))
        {
            return "-";
        }

        else
        {
            return $"Location Key: {Key}" + Environment.NewLine + CityName + $" is located at {Region}, {Country}";
        }
        
    }
}

string keyWord;
public string Keyword
{
    get
    {
        return keyWord;
    }

    set
    {
        keyWord = value;
        OnPropertyChanged("Keyword");
    }
}

string key;
public string Key
{
    get
    {
        return key;
    }

    set
    {
        key = value;
        OnPropertyChanged("Key");
    }
}

string cityName;
public string CityName
{
    get
    {
        return cityName;
    }

    set
    {
        cityName = value;
        OnPropertyChanged("CityName");
    }
}

string region;
public string Region
{
    get
    {
        return region;
    }

    set
    {
        region = value;
        OnPropertyChanged("Region");
    }
}

string country;
public string Country
{
    get
    {
        return country;
    }

    set
    {
        country = value;
        OnPropertyChanged("Country");
    }
}

bool isBusy;
public bool IsBusy
{
    get
    {
        return isBusy;
    }

    set
    {
        isBusy = value;
        OnPropertyChanged("IsBusy");
    }
}
                            

Let's have the command interface right here to handle button event on search.


public ICommand CitySearchCommand { get; set; }

This time, we're going to actually create our method to handle the web service call and parsing of data. Create a method and name it CitySearch. For us to be able to use JArray.Parse, we've to install in our project the Newtonsoft.Json from NuGet Package Manager.


private async void CitySearch()
{
    IsBusy = true;

    var apiUri = cityURL + "search?apikey=" + APIKey + "&q=" + keyWord;

    var httpClient = new System.Net.Http.HttpClient();
    var httpResponse = await httpClient.GetAsync(apiUri);
    var httpResult = httpResponse.Content.ReadAsStringAsync().Result;

    var httpData = JArray.Parse(httpResult);

    foreach(var data in httpData)
    {
        Key = (string)data["Key"];
        CityName = (string)data["EnglishName"];

        var region = (JObject)data["Region"];
        Region = (string)region["EnglishName"];

        var country = (JObject)data["Country"];
        Country = (string)country["EnglishName"];

        OnPropertyChanged(nameof(DisplayMessage));

        break;
    }

    IsBusy = false;
}

Lastly, on WeatherViewModel main method, assign CitySearch method as the command parameter of the CitySearchCommand.


public WeatherViewModel()
{
    CitySearchCommand = new Command(CitySearch);
}

And here's the entire WeatherViewModel class setup should look like.


class WeatherViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    static string APIKey = "[PLACE YOUR API KEY HERE]";
    static string cityURL = "http://dataservice.accuweather.com/locations/v1/cities/";

    public string DisplayMessage
    {
        get
        {
            if (string.IsNullOrEmpty(Key))
            {
                return "-";
            }

            else
            {
                return $"Location Key: {Key}" + Environment.NewLine + CityName + $" is located at {Region}, {Country}";
            }
            
        }
    }

    string keyWord;
    public string Keyword
    {
        get
        {
            return keyWord;
        }

        set
        {
            keyWord = value;
            OnPropertyChanged("Keyword");
        }
    }

    string key;
    public string Key
    {
        get
        {
            return key;
        }

        set
        {
            key = value;
            OnPropertyChanged("Key");
        }
    }

    string cityName;
    public string CityName
    {
        get
        {
            return cityName;
        }

        set
        {
            cityName = value;
            OnPropertyChanged("CityName");
        }
    }

    string region;
    public string Region
    {
        get
        {
            return region;
        }

        set
        {
            region = value;
            OnPropertyChanged("Region");
        }
    }

    string country;
    public string Country
    {
        get
        {
            return country;
        }

        set
        {
            country = value;
            OnPropertyChanged("Country");
        }
    }

    bool isBusy;
    public bool IsBusy
    {
        get
        {
            return isBusy;
        }

        set
        {
            isBusy = value;
            OnPropertyChanged("IsBusy");
        }
    }

    public ICommand CitySearchCommand { get; set; }

    public WeatherViewModel()
    {
        CitySearchCommand = new Command(CitySearch);
    }

    private async void CitySearch()
    {
        IsBusy = true;

        var apiUri = cityURL + "search?apikey=" + APIKey + "&q=" + keyWord;

        var httpClient = new System.Net.Http.HttpClient();
        var httpResponse = await httpClient.GetAsync(apiUri);
        var httpResult = httpResponse.Content.ReadAsStringAsync().Result;

        var httpData = JArray.Parse(httpResult);

        foreach(var data in httpData)
        {
            Key = (string)data["Key"];
            CityName = (string)data["EnglishName"];

            var region = (JObject)data["Region"];
            Region = (string)region["EnglishName"];

            var country = (JObject)data["Country"];
            Country = (string)country["EnglishName"];

            OnPropertyChanged(nameof(DisplayMessage));

            break;
        }

        IsBusy = false;
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new 
            PropertyChangedEventArgs(propertyName));
    }
}

That's enough for our ViewModel so far. Now, let's create our View (layout page). Add new XAML Content Page and name it MainPage. Let's add an Entry to accept user input and a Button to trigger the CitySearchCommand. The ActivityIndicator tells the user that "Hey! Wait for a second, I'm still searching!". Add Label control to display the city information.


<StackStackLayout Padding="10" Spacing="10">
    <StackLayout.BindingContext>
        <Local:WeatherViewModel/>
    </StackLayout.BindingContext>
    
    <StackLayout Orientation="Horizontal">
        
        <Entry Text="{Binding Keyword}"
                Placeholder="Type here..."
                HorizontalOptions="FillAndExpand"></Entry>

        <Button Text="SEARCH"
                Command="{Binding CitySearchCommand}"></Button>

    </StackLayout>

    <ActivityIndicator IsRunning="{Binding IsBusy}" IsVisible="{Binding IsBusy}"></ActivityIndicator>

    <Label Text="City Information"
                FontAttributes="Bold"
                FontSize="Large"></Label>

    <Label Text="{Binding DisplayMessage}"
                FontSize="Medium"></Label>

</StackLayout>

Let's run it!




At this point, our app is now capable of searching and able to display city's continent and country. Now, we're going to identify the searched city's current weather condition and temperature by using other AccuWeather service. This time, we're going to use AccuWeather Current Conditions web service. This service requires Location Key and API Key parameters.


Resource URL

http://dataservice.accuweather.com/currentconditions/v1/264885?apikey=J30ZRfw8QYBfrgQWnDJYNOxpV8e8vx1O

Response



To consume this data in our app, let's go back to our WeatherViewModel class and add a new string variable to hold the conditions web service's url.


static string conditionsURL = "http://dataservice.accuweather.com/currentconditions/v1/";

And add the following string properties below:


public string DisplayWeatherMessage
{
    get
    {
        if (string.IsNullOrEmpty(WeatherText))
        {
            return "-";
        }

        else
        {
            return WeatherText + " " + Temperature;
        }

    }
}

string weatherText;
public string WeatherText
{
    get
    {
        return weatherText;
    }

    set
    {
        weatherText = value;
        OnPropertyChanged("WeatherText");
    }
}

string temperature;
public string Temperature
{
    get
    {
        return temperature;
    }

    set
    {
        temperature = value;
        OnPropertyChanged("Temperature");
    }
}

Let's create new method to handle the web service call and parsing of Current Conditions web service data. Name it as CurrentCondition.


private async void WeatherCondition()
{
    IsBusy = true;

    var apiUri = conditionsURL + Key + "?apikey=" + APIKey;

    var httpClient = new System.Net.Http.HttpClient();
    var httpResponse = await httpClient.GetAsync(apiUri);
    var httpResult = httpResponse.Content.ReadAsStringAsync().Result;

    var httpData = JArray.Parse(httpResult);

    foreach (var data in httpData)
    {
        WeatherText = (string)data["WeatherText"];

        var temperature = (JObject)data["Temperature"];
        var metric = (JObject)temperature["Metric"];

        Temperature = (string)metric["Value"];
        OnPropertyChanged(nameof(DisplayWeatherMessage));
        break;
    }

    IsBusy = false;
}

This time, we've to modify our CitySearch method to call our newly created WeatherCondition method.


private async void CitySearch()
{
    IsBusy = true;

    var apiUri = cityURL + "search?apikey=" + APIKey + "&q=" + keyWord;

    var httpClient = new System.Net.Http.HttpClient();
    var httpResponse = await httpClient.GetAsync(apiUri);
    var httpResult = httpResponse.Content.ReadAsStringAsync().Result;

    var httpData = JArray.Parse(httpResult);

    foreach(var data in httpData)
    {
        Key = (string)data["Key"];
        CityName = (string)data["EnglishName"];

        var region = (JObject)data["Region"];
        Region = (string)region["EnglishName"];

        var country = (JObject)data["Country"];
        Country = (string)country["EnglishName"];
        OnPropertyChanged(nameof(DisplayMessage));

        break;
    }

    IsBusy = false;

    //PLACE THE WeatherCondition METHOD HERE
    WeatherCondition();
}

We're going to edit our MainPage.xaml and we'll be adding new Label control to display weather condition message.


<Label Text="Weather Condition"
    FontAttributes="Bold"
    FontSize="Large"></Label>

<Label Text="{Binding DisplayWeatherMessage}"
    FontSize="Medium"></Label>

Let's run it one more time!




And I guess that's it! We're now able to search for a city, display it's information and it's current weather condition. The idea of consuming RESTful web service is to actually retrieve information from a specific data source via World Wide Web and taking advantage of it on how you're going to work with that information. AccuWeather is just one of the open-source third-party web services, you might want to research more web services available for you to practice more on this topic. You may also create your own web service and there are some good tutorials online as well.


Here's the GitHub repo of this demo:

https://github.com/cedricgabrang/SimpleWeatherApp

Until next time! Happy Xamarin.Forms coding!