Tutorial iOS: Crear un acortador de URL

shorturlok

En este tutorial vamos a ver cuan sencillo resulta crear una app iOS que sea un acortador de URLs utilizando la API pública de Bit.ly; y empleando para ello nuestra propia subclase heredada de Xojo.Net.HTTPSocket, así como la instrucción Declare mediante la cual podemos realizar llamadas directas a las API de Cocoa Touch en este caso.De hecho, el uso de los Declare es fundamental aquí dado que contamos únicamente con el conjunto de clases y funciones disponibles en el nuevo Framework de Xojo, lo que significa que no podemos emplear por ejemplo la función EncodeURLComponent disponible en el antiguo framework, y que resulta imprescindible a la hora de sustituir los caracteres no permitidos en la formación de los URL por su valor equivalente en notación hexadecimal.

Además, también utilizaremos la instrucción Declare para acceder al Portapapeles general de iOS de modo que podamos copiar sobre él el URL acortado por nuestra aplicación, para que pueda utilizarse en el resto de aplicaciones instaladas sobre el dispositivo.

Eso sí, antes de empezar asegúrate de que tienes instalado Xcode en tu Mac, dado que es imprescindible para que puedas ejecutar la aplicación sobre el Simulador de iOS (Xcode es gratuito y se puede descargar desde la Mac App Store). También has de contar con tu propia clave OAuth general, empleada a la hora de acceder a los métodos expuestos por la API web de Bit.ly, y que has de generar creando una cuenta en el servicio Bit.ly. Es un proceso sumamente sencillo y que puedes realizar desde este enlace.

Por supuesto, si quieres seguir el tutorial también has de tener instalado Xojo en tu Mac. Recuerda que siempre puedes descargar y utilizar de forma gratuita la versión más reciente de Xojo desde este enlace.

Con estos requisitos cumplidos, ¡es el momento de ponernos en marcha!

Diseñar la Interfaz iOS

Ejecuta Xojo y en el Selector de Aplicaciones, elige la plantilla para crear una nueva aplicación iOS. Como resultado, se abrirá el IDE presentando todos los elementos que permiten tener una aplicación completamente funcional. De hecho, si pulsas el botón de ejecutar, esta se ejecutará sin ningún problema utilizando el propio Simulador de Xcode. Eso sí, no hará nada puesto que carece de funcionalidad real.

Selecciona el elemento View1 en el Navegador de Proyecto (la columna vertical situada a la izquierda de la ventana del IDE). Dicha acción presentará como resultado la Vista añadida por omisión al proyecto en el Editor de Plantillas. A continuación, pulsa sobre el botón Library, lo cual te permitirá obtener un vistazo de todos los objetos (componentes) gráficos disponibles a la hora de crear una interfaz de usuario para tu app iOS, tal y como se muestra en la siguiente imagen.

Componentes gráficos para crear Interfaz de Usuario en apps iOS.
Componentes gráficos para crear Interfaz de Usuario en apps iOS.

Arrastra el control TextField (lo encontrarás en el apartado Inputs) desde la Librería sobre la parte superior de la vista mostrada en el editor de plantillas. Puedes utilizar el apuntador y las guías para ajustar su posición y tamaño de modo que quede de forma similar a como se presenta en la siguiente imagen.

Campo de Texto añadido a la vista del proyecto iOS.
Campo de Texto añadido a la vista del proyecto iOS.

Selecciona el TextField recién añadido y pulsa sobre el botón Inspector para acceder al panel que presenta las propiedades asociadas. Aquí, por ejemplo, podemos cambiar atributos como su nombre (el que emplearemos para referirnos al control desde el código), el color del texto, etc. En concreto, utiliza los siguientes valores:

  • Name: txtURL
  • KeyboardType: URL
  • PlaceHolder: http://
  • Text: http://

Añade a continuación un botón (Button) desde la Librería y sitúalo justo al costado derecho del campo de texto añadido en el paso anterior. Utiliza los manejadores del control para ajustar su tamaño, y pulsa sobre el icono con imagen de lápiz para modificar el texto de la etiqueta mostrada por el de “Go!”, tal y como se muestra en la siguiente imagen.

BotonGo

Aprovecharemos para que nuestra app muestre la página web correspondiente al URL utilizado, y para ello utilizaremos el componente HTMLViewer que puedes añadir también a la vista arrastrándolo desde la Librería y ajustando su posición tal y como se muestra en la siguiente imagen.

HTMLViewer

Por último, añadiremos un par de etiquetas de texto (Label) y un botón adicional (Button) en la franja inferior de la Vista. Estas serán las etiquetas encargadas de mostrar el URL acortado y el botón será el encargado de copiarlo al Portapapeles general de iOS en el dispositivo. Para ello, arrastra dichos controles desde la Librería, ajustando sus tamaños y posición tal y como se muestra en la siguiente imagen:

ShortenedArea

Selecciona la etiqueta central, que utilizaremos para mostrar el URL acortado, y cambia los siguientes valores en el Panel Inspector asociado:

  • Name: lblShortURL
  • Text: (vacío)

Con esto hemos completado el diseño de la interfaz de usuario de la aplicación. De hecho puedes pulsar sobre el botón Run del IDE de Xojo para ejecutarla sobre el Simulador de Xojo. Comprobarás que es totalmente funcional, pero que no hace nada de interés puesto que aun no hemos escrito ni una sola línea de código.

SimuladorVuelve al IDE de Xojo, detén la ejecución de la aplicación y vamos a encargarnos de la lógica de la aplicación escribiendo para ello la clase encargada de comunicar con la API de Bit.ly.

Comunicar con la API de Bit.ly

Es el momento de crear nuestra clase de comunicación la API de Bit.ly. Es decir, la responsable de realizar la petición web (vía protocolo HTTP) al método expuesto por la API remota y también de recibir los resultados. Y cuando se trata de comunicaciones web en las que deseamos trabajar con la respuesta recibida debemos de utilizar una subclase derivada de la clase Xojo.Net.HTTPSocket, dado que es en nuestra subclase donde añadiremos el comportamiento específico sobre el encontrado en la clase base.

Selecciona Insert > Class para añadir una nueva clase al proyecto. En el Navegador de Proyecto, selecciona la clase recién añadida para acceder al Panel Inspector asociado y cambia los siguientes parámetros:

  • Name: BITLYSocket
  • Super: Xojo.Net.HTTPSocket

A continuación pulsa sobre el icono ‘+’ de la barra de herramientas del IDE y elige la opción Event Handler, tal y como se muestra en la siguiente imagen:

SocketEventHandler

En la ventana resultante, selecciona el evento PageReceived y pulsa OK. La acción presentará el evento recién añadido bajo el apartado Event Handlers de nuestra clase BITLYSocket. En concreto, este es el evento que se disparará y cuyo código se ejecutará cada vez que nuestro socket reciba datos como resultado de nuestras petiticiones HTTP.

Introduce el siguiente código en el Editor de Código asociado con dicho manejador de evento:

if HTTPStatus = 200 then
dim data as Dictionary = xojo.data.ParseJSON( xojo.core.TextEncoding.UTF8.ConvertDataToText(content))
dim dataurl as Dictionary = data.Value("data")

raiseevent URLShortened(dataurl.Value("url"))
end

Lo primero que comprobamos es el código de estado devuelto para ver si el resultado a nuestra petición es correcto (200). A continuación asiganamos a la variable de diccionario ‘data’ el resultado de parsear los datos devueltos a nuestra petición en formato JSON.

La tercera línea es la encargad de crear una nueva variable de diccionario, ‘dataurl’, a la cual se asigna el contenido en el que realmente estamos interesados, y al cual podemos acceder utilizando la clave “data”. Por último, lanzamos un nuevo evento sobre nuestra propia clase (no definido aún, lo haremos a continuación), pasando como parámetro el URL acortado que podemos obtener utilizando la clave “url” sobre el diccionario “dataurl”.

Ten en cuenta que estas claves se corresponden a la estructura de los datos devueltos en el bloque JSON proporcionado por Bit.ly, cuya estructura está documentada y puedes consultar en el sitio web de la API para cada uno de los métodos soportados.

Para añadir un nuevo evento a nuestra clase, vuelve a pulsar el botón ‘+’ en la barra de herramientas, eligiendo en esta ocasión la opción de menú Event Definition, tal y como se muestra en la siguiente imagen:

SocketEventDefinition

A diferencia de lo que ocurre con los métodos, la definición de eventos sólo permite indicar su signatura. Es decir, el nombre del evento creado así como la cantidad, nombre y tipo de los parámetros recibidos y el tipo devuelto (si procede). Después de todo, la implementación del código será independiente y, por lo general, única para cada uno de los “consumidores” de la clase; es decir, las instancias u objetos creados a partir de la clase que decidan implementar dicho evento.

En nuestro caso, el evento recién añadido será el responsable de pasar al consumidor de la clase el URL acortado (tal y como hemos visto en el código anterior) para que este decida qué quiere hacer con él. En nuestra aplicación, asignarlo al atributo de texto de la etiqueta central en la parte inferior de nuestra interfaz de usuario.

Selecciona el evento recién añadido para acceder al Panel Inspector asociado y modifica los siguientes atributos:

  • Event Name: URLShortened
  • Parameters: URL as Text

Realizar la petición HTTP en iOS

Con la clase BITLYSocket aun seleccionada, es el momento de añadir un nuevo método que nos servirá como punto de entrada para que el consumidor de la clase nos indique qué URL desea acortar. Para ello, vuelve a pulsar sobre el botón “+” de la barra de herramientas del IDE, seleccionando en esta ocasión la opción Method del menú.

SocketMethod

Utiliza los siguientes valores en el Panel Inspector asociado:

  • Method Name: ShortURL
  • Parameters: URL as Text

E introduce el siguiente código en el Editor de Código asociado con el método recién creado:

Declare Function CFURLCreateStringByAddingPercentEscapes lib "Foundation" (allocator as Ptr, origString as CFStringRef , charactersToLeaveUnescaped as CFStringRef , legalURLCharactersToBeEscaped as cfStringRef,encoding as uint32) as CFStringRef

dim encodedURL as text = CFURLCreateStringByAddingPercentEscapes(nil,url,nil,nil,&h08000100)

dim compossedURL as text = "https://api-ssl.bitly.com/v3/shorten?access_token="+kToken+"&longUrl="+encodedURL

me.Send("GET", compossedURL)

Aquí nos encontramos con el primer Declare. En concreto es el que nos permite llamar a la función CFURLCreateStringByAddingPercentEscapes para sustituir los caracteres no permitidos en URL por su valor de escape correspondiente, tarea que hacemos precisamente en la segunda línea del código llamando a dicha función y guardando el resultado en la variable encodedURL.

La tercera línea es donde componemos el URL para llamara a la API de Bit.ly, pasando nuestra clave OAuth general (representada aquí mediante la constante kToken (crea tu propia constante en la clase y asigna el valor), y añadiendo en la parte final de la cadena la variable con el URL a acortar debidamente tratado con los caracteres de escape.

Por último, la petición propiamente dicha se realiza utilizando el método Send de la clase HTTPSocket, donde el primer parámetro se corresponde con el método que deseamos utilizar (“GET”, en este caso), y el segundo parámetro es el URL de la petición HTTP.

Con esto, ¡hemos completado la definición de nuestra clase!

¡Poniendo todo en marcha!

Tan solo quedan los detalles, y el primero es añadir una instancia (objeto de clase) de nuestra recién creada clase. Para ello vuelve a seleccionar el objeto View1 en el editor de plantillas y, a continuación, arrastra el icono de nuestra clase BITLYSocket desde el Navegador de Proyecto hacia la parte inferior del Editor de Plantillas. Al soltar verás que se habrá creado una nueva instancia BITLYSocket1 y que, de hecho, también figura bajo la jerarquía Controls de la Vista “View1”.

Selecciona precisamente la instancia BITLYSocket1 bajo Controls en View1 y accede al menú para añadir un nuevo Manejador de Evento. Verás que ahora también figura en el listado el evento creado por nosotros. Selecciónalo y confirma para que se añada sobre la instancia.

BITLYSocketEvent

En el Editor de Código resultante, introduce la siguiente línea:

lblShortURL.Text = url

De esta forma estaremos asignando a la propiedad Text de la etiqueta el texto recibido como resultado de nuestra petición a la API de Bit.ly; en definitiva, el URL acortado.

Pero para que este evento llegue a dispararse en algún momento, lo primero que tenemos que realizar es la petición propiamente dicha. Selecciona el botón Button1, añade el Manejador de Evento Action (este es el que se dispara cuando se pulsa sobre él), e introduce el siguiente código:

  HTMLViewer1.LoadURL(txtURL.Text)
  
  BITLYSocket1.shortURL(txtURL.Text)

En primer lugar cargamos la página web correspondiente al URL introducido y, en la segunda línea de código, invocamos el método ShortURL pasando como parámetro el URL introducido por el usuario en el campo de texto. ¡Esta línea de código es lo que pone en marcha el funcionamiento de nuestro socket de comunicación!

Prueba a ejecutar el proyecto e introducir un URL. ¡Verás como se carga la página web correspondiente y se muestra el URL acortado en la parte inferior de la interfaz!

AppWorking

La app funcionará sin problemas con iOS 8, pero no ocurre lo mismo con iOS 9 o el más reciente iOS 10. El motivo es que a partir de iOS 9 Apple obliga a que todas las peticiones de carga de páginas web o comunicaciones con APIs remotas se realicen utilizando el protocolo seguro HTTPS. En el caso de nuestra subclase creada a partir de HTTPSocket esto no es un problema, después de todo Bit.ly utiliza HTTPS en la comunicación con su API.

El problema real ocurre al intentar cargar cualquier página web en el control HTMLViewer en iOS 9 o posterior. Para solucionarlo tenemos que implementar la solución encontrada en esta entrada del blog de Xojo. Básicamente, has de crear un archivo de texto “Info.Plist” con los siguientes contenidos y arrastrarlo sobre el Navegador de Proyectos del IDE. De esta forma Xojo lo combinará con el archivo Info.plist global generado durante la compilación de la App.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSAppTransportSecurity</key>
<dict>
<!-- Include to allow all connections; avoid if possible -->
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict>
</plist>

Copiar al Portapapeles

Queda un último detalle. Si pulsas sobre el botón Copy… simplemente no hace nada, puesto que no hemos añadido ningún evento de acción.

Selecciona el elemento Button2 en el Navegador de Proyecto y añade el Manejador de Evento Action, escribiendo el siguiente código en el Editor de Código asociado:

declare sub setValue lib "UIKit" selector "setValue:forPasteboardType:" (obj_id as ptr, value as ptr, pasteboardType as CFStringRef)
declare function generalPasteboard lib "UIKIt" selector "generalPasteboard" (clsRef as ptr) as ptr
declare function NSClassFromString lib "Foundation" (clsName as cfstringref) as ptr
declare function stringWithString lib "Foundation" selector "stringWithString:" (clsRef as ptr, str as cfstringref) as ptr

setValue(generalPasteboard(NSClassFromString("UIPasteboard")), stringWithString(NSClassFromString("NSString"),lblShortURL.Text), "public.text")

Esta es la solución que te permite copiar el texto del URL acortado en el Portapepeles del sistema bajo iOS. Dicha solución, construida a base de Declare con llamadas a las API nativas Cocoa Touch y Foundation está extraída de este hilo del foro de Xojo.

Ahora, cuando pulses el botón Copy ya contarás con el URL acortado en el Portapapeles.

En definitiva, a lo largo de este tutorial hemos podido ver lo sencillo que resulta comunicar en nuestras apps iOS con API Web que devuelvan los datos en formato JSON; así como lo potente y útil que resulta la instrucción Declare para llamar a métodos y funciones específicos de los frameworks nativos desde nuestro código Xojo, capturando así aquellas capacidades que aun no están implementadas en el propio framework de Xojo.

Un comentario en “Tutorial iOS: Crear un acortador de URL

Deja un comentario

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