Xamarin Forms: Consumiendo un servicio REST basado en JSON

En esta publicación, veremos cómo podemos consumir un servicio REST desde una aplicación basada en Xamarin Forms. Esta es una tarea muy común para los desarrolladores Xamarin, por lo que aprender a hacerlo, debe convertirse en una tarea habitual. Sin más, vamos a la parte práctica.

¡Adquiere el Máster en Xamarin Forms!

Antes de iniciar, te invito a visitar la página de la Membresía de mi academia, donde podrás encontrar:

Cursos y talleres de Xamarin
Cursos y talleres en C#
Cursos y talleres de Blazor
– Cursos y talleres de ASP.NET
– Cursos y talleres en muchas otras tecnologías

¡Todo al precio más bajo posible por tiempo limitado!

curso de xamarin

Consumiendo un Servicio REST desde Xamarin Forms – Creando el modelo

Para llevar a cabo el consumo de un servicio web basado en Json, en primer lugar, debemos tener disponible el servicio mismo. Para llevar a cabo esta demostración, utilizaré los servicios alojados en este enlace.

Esta página nos permite utilizar más de 30 servicios web diferentes, cuya respuesta puede ser XML, JSON, RDF, CSV, TXT, RSS ó KML, basta con dar click sobre el enlace requerido para que se nos muestre un ejemplo de la respuesta del servicio. Yo utilizaré el servicio findNearByWeather.

Screen Shot 04-20-16 at 09.52 AM
Screen Shot 04-20-16 at 09.53 AM

Para iniciar el desarrollo de la aplicación, debemos crear un proyecto de tipo Xamarin.Forms, de tipo Portable:

Screen Shot 04-20-16 at 10.14 AM

Una vez creado el proyecto, crearemos 1 carpeta llamada Model, y dentro de esta, crearemos una clase que aloje las propiedades que correspondan al modelo del archivo Json, a la cual llamaremos WeatherObservation.

Ahora bien, para llevar a cabo la creación de las propiedades dentro de la clase, podríamos pensar que debemos de escribirlas a mano una por una, sin embargo, Visual Studio nos ofrece la posibilidad de generar el modelo a partir del archivo json. Una vez copiado el archivo json, debemos pegarlo a través del menú Edit –> Paste Special –> Paste JSON As Classes, lo cual nos generará el siguiente código:

namespace JsonWebServices.Model
{

    public class Rootobject
    {
        public Weatherobservation weatherObservation { get; set; }
    }

    public class Weatherobservation
    {
        public int elevation { get; set; }
        public float lng { get; set; }
        public string observation { get; set; }
        public string ICAO { get; set; }
        public string clouds { get; set; }
        public string dewPoint { get; set; }
        public string cloudsCode { get; set; }
        public string datetime { get; set; }
        public string countryCode { get; set; }
        public string temperature { get; set; }
        public int humidity { get; set; }
        public string stationName { get; set; }
        public string weatherCondition { get; set; }
        public int windDirection { get; set; }
        public int hectoPascAltimeter { get; set; }
        public string windSpeed { get; set; }
        public float lat { get; set; }
    }

}

A continuación, renombraremos la clase Rootobject por WeatherResult. y modificaremos las propiedades para que inicien con mayúsculas, quedando nuestro modelo de la siguiente forma:

namespace JsonWebServices.Model
    {
        public class WeatherResult
        {
            public Weatherobservation WeatherObservation { get; set; }
        }

        public class Weatherobservation
        {
            public int Elevation { get; set; }
            public float Lng { get; set; }
            public string Observation { get; set; }
            public string Icao { get; set; }
            public string Clouds { get; set; }
            public string DewPoint { get; set; }
            public string CloudsCode { get; set; }
            public string Datetime { get; set; }
            public string CountryCode { get; set; }
            public string Temperature { get; set; }
            public int Humidity { get; set; }
            public string StationName { get; set; }
            public string WeatherCondition { get; set; }
            public int WindDirection { get; set; }
            public int HectoPascAltimeter { get; set; }
            public string WindSpeed { get; set; }
            public float Lat { get; set; }
        }

    }

Cómo consumir un servicio REST desde Xamarin Forms – Creación del ViewModel

El siguiente paso en nuestro proyecto, es crear el ViewModel. En primer lugar, crearemos una carpeta llamada ViewModel, donde alojaremos nuestro código correspondiente al ViewModel.

Crearemos una clase llamda MainPageViewModel, a la cual implementaremos la interfaz: INotifyPropertyChanged, de la siguiente manera:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using JsonWebServices.Annotations;

namespace JsonWebServices.ViewModel
{
    public class MainPageViewModel : INotifyPropertyChanged
    {
        #region InotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion
    }
}

Para poder llevar a cabo la notificación de un cambio en una de las propiedades para poder comunicársela a la interfaz gráfica a través de un binding, debemos crear una propiedad  por cada valor que queramos notificar a la interfaz gráfica, seguido de la llamada al método OnPropertyChanged(), como en el siguiente ejemplo:

        private string _stationName;
        public string StationName
        {
            get
            {
                return _stationName;
            }

            set
            {
                _stationName = value;
                OnPropertyChanged();
            }
        }

Para ejemplos demostrativos, implementaremos las propiedades de StationName, Elevation, Temperature y Humidity, de la siguiente forma

        #region Properties
        private string _stationName;
        private int _elevation;
        private string _temperature;
        private int _humidity;
        public string StationName
        {
            get
            {
                return _stationName;
            }

            set
            {
                _stationName = value;
                OnPropertyChanged();
            }
        }
        public int Elevation
        {
            get
            {
                return _elevation;
            }

            set
            {
                _elevation = value;
                OnPropertyChanged();
            }
        }

        public string Temperature
        {
            get
            {
                return _temperature;
            }

            set
            {
                _temperature = value;
                OnPropertyChanged();
            }
        }

        public int Humidity
        {
            get
            {
                return _humidity;
            }

            set
            {
                _humidity = value;
                OnPropertyChanged();
            }
        }
        #endregion

Para la siguiente parte, debemos instalar un par de paquetes de nuget, los cuales puedes instalar a través de la Consola de paquetes de nuget, apuntando al proyecto Portable:

Screen Shot 04-25-16 at 09.46 AM

los cuales son:

Install-Package Microsoft.Net.Http
Install-Package Newtonsoft.Json

Posteriormente, debemos crear un método llamado GetWeatherAsync que se encargue de llevar a cabo la solicitud al sitio web, a través de una instancia de HttpClient. y su posterior deserialización de la siguiente forma:

        public async Task GetWeatherAsync(string url)
        {
            //Creamos una instancia de HttpClient
            var client = new HttpClient();
            //Asignamos la URL
            client.BaseAddress = new Uri(url);
            //Llamada asíncrona al sitio
            var response = await client.GetAsync(client.BaseAddress);
            //Nos aseguramos de recibir una respuesta satisfactoria
            response.EnsureSuccessStatusCode();
            //Convertimos la respuesta a una variable string
            var jsonResult = response.Content.ReadAsStringAsync().Result;
            //Se deserializa la cadena y se convierte en una instancia de WeatherResult
            var weather = JsonConvert.DeserializeObject<WeatherResult>(jsonResult);
            //Asignamos el nuevo valor de las propiedades
            SetValue(weather);
        }

Por último, agregaremos un método llamado SetValue, que nos servirá para actualizar las propiedades de la clase contenida en el ViewModel, de la siguiente forma:

        private void SetValue(WeatherResult weather)
        {
            var stationName = weather.WeatherObservation.StationName;
            var elevation = weather.WeatherObservation.Elevation;
            var temperature = weather.WeatherObservation.Temperature;
            var humidity = weather.WeatherObservation.Humidity;

            StationName = stationName;
            Elevation = elevation;
            Temperature = temperature;
            Humidity = humidity;
        }

El resultado final de la clase, deberá verse de la siguiente forma:

using System;
using System.ComponentModel;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using JsonWebServices.Annotations;
using JsonWebServices.Model;
using Newtonsoft.Json;

namespace JsonWebServices.ViewModel
{
    public class MainPageViewModel : INotifyPropertyChanged
    {
        #region Properties
        private string _stationName;
        private int _elevation;
        private string _temperature;
        private int _humidity;
        public string StationName
        {
            get
            {
                return _stationName;
            }

            set
            {
                _stationName = value;
                OnPropertyChanged();
            }
        }
        public int Elevation
        {
            get
            {
                return _elevation;
            }

            set
            {
                _elevation = value;
                OnPropertyChanged();
            }
        }

        public string Temperature
        {
            get
            {
                return _temperature;
            }

            set
            {
                _temperature = value;
                OnPropertyChanged();
            }
        }

        public int Humidity
        {
            get
            {
                return _humidity;
            }

            set
            {
                _humidity = value;
                OnPropertyChanged();
            }
        }
        #endregion

        #region Methods
        public async Task GetWeatherAsync(string url)
        {
            //Creamos una instancia de HttpClient
            var client = new HttpClient();
            //Asignamos la URL
            client.BaseAddress = new Uri(url);
            //Llamada asíncrona al sitio
            var response = await client.GetAsync(client.BaseAddress);
            //Nos aseguramos de recibir una respuesta satisfactoria
            response.EnsureSuccessStatusCode();
            //Convertimos la respuesta a una variable string
            var jsonResult = response.Content.ReadAsStringAsync().Result;
            //Se deserializa la cadena y se convierte en una instancia de WeatherResult
            var weather = JsonConvert.DeserializeObject<WeatherResult>(jsonResult);
            //Asignamos el nuevo valor de las propiedades
            SetValue(weather);
        }

        private void SetValue(WeatherResult weather)
        {
            var stationName = weather.WeatherObservation.StationName;
            var elevation = weather.WeatherObservation.Elevation;
            var temperature = weather.WeatherObservation.Temperature;
            var humidity = weather.WeatherObservation.Humidity;

            StationName = stationName;
            Elevation = elevation;
            Temperature = temperature;
            Humidity = humidity;
        }
        #endregion

        #region InotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion
    }
}

Pasos para consumir un servicio REST desde Xamarin Forms – Creación de la Vista

Para finalizar este proyecto, crearemos el formulario de la interfaz gráfica, y haremos los ajustes finales para tener nuestra aplicación corriendo.

En primer lugar, crearemos la última carpeta que nos hace falta llamada View:

image

En la nueva carpeta crearé un nuevo elemento del tipo Forms Xaml Page al cual llamaremos MainPage:

image

En el formulario creado, usaremos el siguiente código para el archivo MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="JsonWebServices.View.MainPage">
    <ContentPage.Content>
        <StackLayout Padding="30">
            <StackLayout Orientation="Horizontal">
                <Label Text="Latitude" WidthRequest="130"></Label>
                <Entry x:Name="txtLatitude" WidthRequest="150"></Entry>
            </StackLayout>
            <StackLayout Orientation="Horizontal">
                <Label Text="Longitude" WidthRequest="130"></Label>
                <Entry x:Name="txtLongitude" WidthRequest="150"></Entry>
            </StackLayout>
            <Button x:Name="btnSearch" Clicked="OnClicked" WidthRequest="75" Text="Search" TextColor="White" BackgroundColor="Blue"/>
            <StackLayout Orientation="Horizontal">
                <Label Text="Location: " TextColor="Green" WidthRequest="130"/>
                <Label Text="{Binding StationName}"></Label>
            </StackLayout>
            <StackLayout Orientation="Horizontal">
                <Label Text="Elevation: " TextColor="Green" WidthRequest="130"/>
                <Label Text="{Binding Elevation}"/>
            </StackLayout>
            <StackLayout Orientation="Horizontal">
                <Label Text="Temperature: " TextColor="Green" WidthRequest="130"/>
                <Label Text="{Binding Temperature}"></Label>
            </StackLayout>
            <StackLayout Orientation="Horizontal">
                <Label Text="Humidity: " TextColor="Green" WidthRequest="130"/>
                <Label Text="{Binding Humidity}"></Label>
            </StackLayout>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Por otra parte, en el archivo MainPage.xaml.cs, debemos definir una variable que será la instancia del ViewModel de la siguiente manera:

public MainPageViewModel vm;

Esta variable la inicializaremos dentro del constructor:

 vm = new MainPageViewModel();

De igual forma, dentro del constructor debemos asignar el contexto de datos a dicha variable de la siguiente forma:

 BindingContext = vm;

Por último, crearemos el manejador de eventos que hará la llamada al servicio web, en primer lugar, obtendremos la información de las cajas de texto, paso seguido definiremos la url del servicio web, y al último haremos la llamada a través del método especificado:

        public async void OnClicked(object sender, EventArgs e)
        {
            var longitude = double.Parse(txtLongitude.Text);
            var latitude = double.Parse(txtLatitude.Text);

            var url = string.Format(@"http://api.geonames.org/findNearByWeatherJSON?formatted=true&lat={0}&lng={1}&username=demo&style=full", latitude, longitude);
            await vm.GetWeatherAsync(url);
        }

quedando de la siguiente manera el archivo:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using JsonWebServices.ViewModel;
using Xamarin.Forms;

namespace JsonWebServices.View
{
    public partial class MainPage : ContentPage
    {
        public MainPageViewModel vm;
        public MainPage()
        {
            vm = new MainPageViewModel();
            BindingContext = vm;
            InitializeComponent();
        }
        public async void OnClicked(object sender, EventArgs e)
        {
            var longitude = double.Parse(txtLongitude.Text);
            var latitude = double.Parse(txtLatitude.Text);

            var url = string.Format(@"http://api.geonames.org/findNearByWeatherJSON?formatted=true&lat={0}&lng={1}&username=demo&style=full", latitude, longitude);
            await vm.GetWeatherAsync(url);
        }
    }
}

Por último, modificaremos el constructor de la clase App para que asigne a la página MainPage como la página principal:

public App()
{
   // The root page of your application
   MainPage = new MainPage();
}

Una vez terminada la codificación, podemos buscar coordenadas en cualquier aplicación de mapas para probar:

image

Ejecutaremos la aplicación e Insertaremos estas coordenadas, al presionar el botón search, nos mostrará el resultado correspondiente:

image

Definitivamente hay cosas que pueden ser mejoradas, pero queda como tarea para la casa, Espero que esta serie les haya gustado y servido. Recuerden que todo el proyecto lo pueden descargar desde aquí.

Saludos.

Deja un comentario

Tu dirección de correo electrónico no será publicada.