Diciembre 2013 Javier de Silóniz Sandino Especialista iOS en atSistemas Un plugin es especialmente útil cuando necesitamos acceder a características internas del hardware del dispositivo móvil. Creación de plugins para Apache Cordova Un plugin de Cordova/Phonegap sirve como unión entre el WebView que muestra la aplicación híbrida y la plataforma nativa sobre la cual corre. Estos plugins se componen de un interfaz JavaScript común que se usa en todas las plataformas, y de implementaciones nativas que siguen unos interfaces específicos para cada plataforma a los que la parte JavaScript hará referencia. Un plugin nos es útil cuando una determinada funcionalidad no es accesible desde la parte JavaScript de un proyecto Cordova/Phonegap, en especial cuando se trata de acceder a características internas del hardware del dispositivo móvil (ej: cámara, servicios de localización, acelerómetros…) o hacer uso de librerías externas que sólo dispongan de implementación nativa en las plataformas a las que haya que dar soporte. Los plugins siguen todos una arquitectura común, por lo que aunque es posible instalarlos a mano en un proyecto, sin duda no es lo ideal. La versión 3 de Cordova/ Phonegap nos proporciona una serie de metodologías y estructuras que permiten crear e instalar nuevos plugins más fácil y rápidamente. Parte JavaScript Vamos a desarrollar un plugin con una funcionalidad simple para entender cómo funciona su arquitectura así como su uso. Nuestro plugin realizará un simple “eco” mediante el cual el usuario del mismo podría envíar una cadena de texto desde la parte JavaScript a la parte nativa del dispositivo. El código nativo a su vez devolverá esta misma cadena de texto a la parte JavaScript mediante los interfaces que Cordova/Phonegap proporciona. El punto de entrada para cualquier plugin es código JavaScript. Uno de los principales motivos por el que los desarrolladores usan Cordova/Phonegap es precisamente poder usar JavaScript y no Java, Objective-C o C#. Esta parte por tanto es la más importante del desarrollo de un plugin, ya que en ella definimos qué funciones tendrá a su disposición el usuario del mismo, el número de parámetros, etc… La definición de la parte JavaScript para nuestro plugin puede ser la siguiente: var cordova = require(‘cordova’); function EchoPlugin() {}; EchoPlugin.prototype.echo = function(options, callback) { options = options || {}; cordova.exec(callback,callback,”EchoPlugin”,”echo”, [{ message: options.message || “” }]); } var echoPlugin = new EchoPlugin(); module.exports = echoPlugin; atsistemas.com Creación de plugins para Apache Cordova La parte más importante de nuestro código JavaScript es la llamada al método cordova.exec. Este método recibe cinco parámetros: El método cordova.exec recibe cinco parámetros: • Callback de éxito • Callback de error • Nombre del servicio • Acción • Argumentos • Callback de éxito: función JavaScript a la que se llamará una vez que se reciba un mensaje de éxito de ejecución desde la parte nativa (ej: si nuestro plugin accede a la cámara para tomar imágenes, se llamaría a este método en caso de que el usuario haya tomado la foto correctamente). • Callback de error: función JavaScript a la que se llamará si se recibe un mensaje de error en la ejecución desde la parte nativa (aprovechando el ejemplo de antes, se llamaría a este método en caso de que el usuario cancelase la toma de la foto). • El nombre del servicio al que se llamará en la parte nativa: generalmente corresponde al nombre de la clase que contiene el código nativo del plugin. Ahondaremos más sobre esto cuando hablemos de la implementación nativa. • Acción: el nombre de la acción a la que se llamará. En general esto coincidirá con un método de la implementación nativa. • Argumentos: parámetros que se pasarán al entorno nativo (en este caso, la cadena de texto que contiene el mensaje al que queremos hacer “eco”). Parte nativa Las implementaciones de los plugins de Cordova/Phonegap varían en función de cada plataforma. Vamos a estudiar aquí dos de los casos más comunes: iOS y Android. • iOS: en esta plataforma los plugins son subclases de CDVPlugin. Deben implementar métodos cuyos nombres se corresponden con los diferentes valores posibles del parámetro “action” de cordova.exec en la prte JavaScript. Estos métodos reciben como único parámetro un objeto CDVInvokedUrlCommand que contiene entre otros datos un array de de parámetros (correspondiente a la que envíamos desde cordova.exec en su último parámetro). A su vez, el plugin devuelve un resultado a la parte JavaScript mediante el uso de un objeto de tipo CDVPluginResult. Veamos la implementación: atsistemas.com Creación de plugins para Apache Cordova - (void)echo:(CDVInvokedUrlCommand *)command { // Obtenemos los parámetros: NSDictionary *parameters = [command.arguments objectAtIndex:0]; NSString *message = [parameters objectForKey:@”message”]; // Y devolvemos el resultado: CDVPluginResult* pluginResult = nil; @try { if (message != nil && [message length] > 0) { // Si el mensaje es válido, devolvemos un resultado positivo: pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ OK messageAsString:message]; [self.commandDelegate sendPluginResult:pluginResultcallbackId:command. callbackId]; } else { // Si no, se devolve un error: pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ ERROR]; [self.commandDelegate sendPluginResult:pluginResultcallbackId:command. callbackId]; } } @catch (NSException* exception) { // Contemplamos la posibilidad de recibir datos erróneos desde la parte JavaScript: pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ JSON_EXCEPTION messageAsString:[exception reason]]; [self.commandDelegate sendPluginResult:pluginResultcallbackId:command. callbackId]; } } •Android: A diferencia de la implementación en iOS, en Android todas las llamadas al plugin pasan por el método CallbackContext. en esta plataforma los plugins son subclases de CordovaPlugin. En este caso, la subclase debe implementar el método execute que recibe como parámetros el nombre de la acción, una array con argumentos y un objeto de tipo CallbackContext. A diferencia de la implementación en iOS, todas las llamadas al plugin pasan por este método y depende de nosotros comprobar las acciones disponibles y redirigir la llamada a los métodos que nos interesen: public boolean execute(String action, JSONArray args, CallbackContext cbc) { try { if (action.equals(“echo”)) { JSONObject obj = args.getJSONObject(0); String message = obj.getString(“message”); if(!message.equals(“”)){ callbackContext.success(); }else{ callbackContext.error(“Mensaje vacío”); } } return true; } catch (JSONException e) { return false; } } atsistemas.com Creación de plugins para Apache Cordova Estructura del plugin Con el objetivo de facilitar tanto su implementación, como su instalación y uso; los plugins de Cordova/Phonegap 3 incorporan una estructura común. El código nativo irá dentro de una carpeta “src” (en diversas subcarpetas, “ios”, “android”…), y el código JavaScript en otra carpeta “www” al mismo nivel. En la raíz de la estructura del plugin se debe encontrar un archivo plugin.xml que proporciona información al instalador de Cordova sobre los añadidos que debe hacer al proyecto para su correcto funcionamiento, así como la localización de las clases nativas, versiones compatibles de Cordova/Phonegap, etc… La estructura básica de nuestro plugin.xml será la siguiente: <?xml version=”1.0” encoding=”UTF-8”?> <plugin xmlns=”http://www.phonegap.com/ns/plugins/1.0” id=”com.hybreed.plugins.echo” version=”1.0.0”> <name>EchoPlugin</name> <js-module src=”www/EchoPlugin.js” name=”EchoPlugin”> <clobbers target=”echoPlugin” /> </js-module> <engines> <engine name=”cordova” version=”>=3.0.0” /> </engines> <!-- ios --> <platform name=”ios”> <config-file target=”config.xml” parent=”/widget/plugins”> <feature name=”LocalNotification”> <param name=”ios-package” value=”EchoPlugin” /> </feature> </config-file> <header-file src=”src/ios/LocalNotificationPlugin.h” /> <source-file src=”src/ios/LocalNotificationPlugin.m” /> </platform> atsistemas.com Creación de plugins para Apache Cordova <platform name=”android”> <source-file src=”src/android/com/phonegap/plugin/echo/EchoPlugin.java” target-dir=”src/com/phonegap/plugin/echoPlugin” /> <config-file target=”res/xml/config.xml” parent=”plugins”> <plugin name=”LocalNotification” value=”com.phonegap.plugin.localnotification.EchoPlugin”/> </config-file> <config-file target=”res/xml/config.xml” parent=”/*”> <feature name=”LocalNotification”> <param name=”android-package” value=”com.phonegap.plugin. localnotification.EchoPlugin” /> </feature> </config-file> </platform> </plugin> La primera parte trata con parámetros generales. Por ejemplo el tag “clobbers” dentro de “js-module” modifica el JavaScript del plugin para incluír referencia a una variable global a la que podremos acceder en todo momento para hacer uso del plugin (en este caso “echoPlugin”). A su vez, el tag “engines” define la versión de Cordova/Phonegap para la que el plugin es compatible. A partir de este punto el archivos se divide en varios bloques para cada plataforma, definidos por el tag “platform”. En este ejemplo tenemos dos, uno para cada plataforma descrita (iOS y Android). En ambos casos usamos primero un tag de tipo “source-file” que utilizamos para hacer referencia a las rutas de los archivos fuente de la parte nativa (que serán copiados al proyecto Cordova durante la instalación del plugin). A continuación hacemos uso del tag “config-file” para modificar o añadir segmentos de los diferentes archivos XML que se incluyen en el plugin. Esto no se limita sólo a archivos propios del proyecto Cordova/ Phonegap, podemos usarlo para modificar el Manifest del proyecto Android en caso de necesitar añadir permisos a la app, por ejemplo. Es una buena práctica definir una metodología común a la hora de tratar las distintas interfaces de nuestros plugins. Instalación y uso Una vez montada la estructura del plugin, y alojada en un repositorio, instalar un plugin en un proyecto Cordova/Phonegap ya existente es tan sencillo como ejecutar el siguiente comando dentro de la carpeta raíz del mismo: cordova plugin add https://ruta-del-repositorio.git atsistemas.com Creación de plugins para Apache Cordova Este comando toma la información del archivo plugin.xml de la raíz, copia los archivos tanto del código nativo como del código JavaScript al proyecto, y realiza los cambios indicados en los XML de configuración. Una vez realizada la instalación, y si no ha ocurrido ningún error, acceder al plugin es tan sencillo como hacer referencia en nuestro código JavaScript a la variable cuyo nombre viene dado por el tag “clobbers” en el plugin.xml. Un ejemplo de uso en nuestro caso sería: echoPlugin.echo({“message” : “¡¡ECO!!”}, function(message) { // Código de callback } ); Aunque no es obligatorio, es una buena práctica definir una metodología común a la hora de tratar las distintas interfaces de nuestros plugins. En este caso, decidimos pasar todos los parámetros del plugin en un diccionario JSON (que será recibido como un diccionario igualmente en el código nativo), y una función callback que será llamada al terminarse de ejecutar el código nativo. atsistemas.com En atSistemas somos más de 500 profesionales dedicados desde 1994 a la consultoría, servicios de IT y desarrollo de software. Nuestros servicios se caracterizan por la flexibilidad y la agilidad, lo que nos permite ayudar a grandes empresas de todos los sectores, aportando conocimiento y experiencia sobre el más amplio abanico de tecnologías. Nuestra cartera de clientes incluye más de 200 de las principales empresas del país, con representación de todos los sectores de actividad, a los que prestamos servicio desde nuestras oficinas de Madrid, Barcelona, Cádiz y Zaragoza. Nuestro portfolio de servicios abarca desde el desarrollo de software a medida hasta la integración de grandes soluciones de software empresarial, en áreas que van desde la más compleja arquitectura de sistemas hasta las soluciones más novedosas de comercio electrónico o aplicaciones móviles. Valle de Alcudia, 3 28230 Las Rozas, Madrid Passeig de Gràcia 55, 8º - 4ª 08007 Barcelona 902 888 902 atsistemas.com [email protected]