[Windows 8] Apps Metro para desarrolladores Windows Phone 7.5 (9 de N)

Hola a todos!

Aquí estamos una vez más con un nuevo artículo de nuestra serie sobre desarrollo de aplicaciones metro en C# y XAML. En esta ocasión vamos a hablar de como asegurarnos de que disponemos de conectividad a internet y como poder usar esta conectividad para consumir servicios.

Comprobar la conectividad

Uno de los requisitos más importantes a tener en cuenta en una aplicación Windows Phone es el de comprobar si tenemos acceso a internet antes de intentar usarla, para así poder avisar al usuario y que nuestra aplicación no falle sin más.

En Windows Phone conseguimos esto haciendo uso de la clase NetworkInterface que nos facilita un método GetIsNetworkAvailable, este método devuelve true si tenemos conexión o false de lo contrario:

   1:  if (NetworkInterface.GetIsNetworkAvailable())
   2:  {
   3:      MessageBox.Show("Network connection available");
   4:  }
   5:  else
   6:  {
   7:      MessageBox.Show("No network connection at this time");
   8:  }

La forma en la que podemos realizar este mismo paso en WinRT es, si cabe, más sencilla. Solo tenemos que registrarnos al evento NetworkStatusChanged y automáticamente obtendremos una notificación cuando el estado de la conexión cambie, momento en el cual podremos preguntar cual es el estado actual usando la clase NetworkInterface del namespace Windows.Networking.Connectivity de forma muy similar a Windows Phone:

   1:  public static bool NetworkAvailable = true;
   2:   
   3:  public App()
   4:  {
   5:      this.InitializeComponent();
   6:      this.Suspending += OnSuspending;
   7:      NetworkInformation.NetworkStatusChanged += NetworkInformation_NetworkStatusChanged;
   8:   
   9:  }
  10:   
  11:  void NetworkInformation_NetworkStatusChanged(object sender)
  12:  {
  13:      if (NetworkInformation.GetInternetConnectionProfile() == null)
  14:      {
  15:          NetworkAvailable = false;
  16:      }
  17:      else
  18:      {
  19:          NetworkAvailable = true;
  20:      }
  21:  }

De esta forma en cualquier parte de nuestra aplicación, normalmente en una viewmodel que vaya a realizar una llamada de algún tipo a un servicio externo, podremos comprobar si tenemos o no internet y avisar al usuario sin necesidad de invertir tiempo en una llamada que va a fallar de antemano.

Un pequeño bug

Aunque todavía lo estoy investigando, he decidido comentarlo aquí, pues ya he podido reproducirlo en varios equipos distintos con la release preview y sería interesante saber si alguien más se encuentra con el mismo problema. El código que he enseñado anteriormente se encuentra en la clase App de la aplicación, si en cualquier punto de esta clase introducimos un MessageDialog, algo así por ejemplo:

   1:  async void NetworkInformation_NetworkStatusChanged(object sender)
   2:  {
   3:      MessageDialog dialog = new MessageDialog("Test");
   4:      var result = await dialog.ShowAsync();
   5:  }

E intentamos ejecutar la aplicación, al desconectar la red y ejecutarse el evento obtendremos el siguiente error:

image

Este mismo código, colocado en cualquier otro lugar fuera de la clase App funciona correctamente, además he podido probarlo en un Visual Studio 11 Beta sobre una consumer preview y no ocurría. Si alguien más puede reproducirlo sería de gran ayuda.

Accediendo a datos “en la nube”

Cuando accedemos a datos en internet estos pueden venir dados por un servicio REST, un servicio SOAP, datos en XML… multitud de formas diferentes de acceder a ellos. Una de las formas más sencillas es el consumir una fuente XML, de un Rss por ejemplo.

En Windows Phone podíamos usar la clase WebClient o HttpWebRequest para obtener un xml de Rss y luego procesarlo, en WinRT tenemos la clase HttpClient, del namespace System.Net.Http, que nos permite realizar la misma tarea, pero con el añadido de poder usar async / await para consumirla de forma más sencilla y sin pelearnos con los callbacks:

   1:  private async void GetRss()
   2:  {
   3:      HttpClient client = new HttpClient();
   4:   
   5:      var response = await client.GetAsync("http://geeks.ms/blogs/jyeray/rss.aspx");
   6:   
   7:      string docString = await response.Content.ReadAsStringAsync();
   8:   
   9:      if (!string.IsNullOrEmpty(docString))
  10:      {
  11:          //...
  12:      }
  13:  }

El método GetAsync nos devuelve una instancia de HttpResponseMessage, de la cual podemos obtener la cadena de texto con el contenido de la respuesta, que en este caso será el XML de nuestro Rss que podremos procesar de forma normal usando LinqToXML y XDocument para recorrerlo y convertir el XML en instancias de clases que podamos consumir.

Otro tipo de servicio muy sencillo de consumir es un servicio WCF, como siempre en Visual Studio, simplemente debemos hacer botón derecho sobre nuestro proyecto, escoger la opción “Add Service Reference” e introducir la url del servicio o hacer click en Discover si lo tenemos en nuestra misma solución:

image

La mayor novedad podemos encontrarla al presionar “Advanced”:

image

Como vemos en la primera parte de la pantalla, por defecto tenemos marcado el check de operaciones asíncronas y en concreto que se generen basadas en Task, es decir, para usar el modelo de Async. si presionamos OK en ambas pantallas y vamos al archivo reference.cs, localizado dentro de la referencia de nuestro servicio:

image

Veremos que en la Interface de nuestro servicio tenemos definidos los métodos asíncronos, basados en Tasks:

   1:  public interface IService1 {
   2:          
   3:      [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IService1/GetData", ReplyAction="http://tempuri.org/IService1/GetDataResponse")]
   4:      System.Threading.Tasks.Task<string> GetDataAsync(int value);
   5:          
   6:      [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IService1/GetDataUsingDataContract", ReplyAction="http://tempuri.org/IService1/GetDataUsingDataContractResponse")]
   7:      System.Threading.Tasks.Task<win8services.SrvReference.CompositeType> GetDataUsingDataContractAsync(win8services.SrvReference.CompositeType composite);
   8:  }

Anteriormente, en la interface de nuestros servicios solo se definían los métodos Begin y End para nuestras operaciones, mientras que el método Async y el evento Completed se definían en la clase Client que implementaba la Interface. Esto daba lugar a que si deseábamos trabajar con la interface, principalmente para poder implementar un cliente “falso” para los test unitarios, teníamos que hacer un pequeño truco para incluir nuestros métodos Async y Completed. Como ahora los métodos asíncronos están definidos en la interface directamente, nos ahorramos este trabajo, pudiendo usarlos directamente desde la misma:

   1:  private async void GetData()
   2:  {
   3:      IService1 service = new Service1Client();
   4:      string response = await service.GetDataAsync(27);
   5:      MessageDialog dlg = new MessageDialog(response);
   6:      var result = await dlg.ShowAsync();
   7:  }

Esto nos permite pasar la interface como una dependencia de nuestra viewmodel por el constructor, de forma que en nuestros test podamos pasar un mock de la misma.

Conclusión

En WinRT se ha simplificado mucho el consumo de servicios WCF al exponer los métodos asíncronos directamente en la interface y también el consumo de otro tipo de datos y servicios mediante la clase HttpClient, sin tener que pelearnos con los callbacks. Esta es una parte esencial de casi toda aplicación moderna, la comunicación con “el mundo exterior”, además el poder tener en todo momento conocimiento del estado de la red nos permite adelantarnos a fallos y dar al usuario información muy útil para su trabajo.

Como ya es costumbre, aquí os dejo el ejemplo de hoy para que podáis jugar con el y tenerlo como referencia.

Un saludo y Happy Coding!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *