Trabajar con Worklight, Parte 2: Cómo desarrollar módulos estructurados y utilizar la característica de memoria caché encriptada sin conexión en IBM Worklight

Descubra cómo dotar a su aplicación móvil con capas de estructura, funcionalidad y seguridad

Esta serie de artículos presenta la plataforma IBM® Worklight® al mostrar cómo puede desarrollar aplicaciones móviles que nivelan una variedad de productos de software IBM. La parte 2 continúa con la descripción del proceso para desarrollar una aplicación Worklight, mostrando algunas mejores prácticas para desarrollar algunas aplicaciones híbridas y presentando la funcionalidad de Worklight llamada memoria caché encriptada sin conexión.

Carlos Andreu , Software Developer, IBM

Carlos Andreu es un desarrollador de software del Grupo de Software de IBM. Actualmente, se encuentra trabajando en la creación de un marco de trabajo para desarrollar aplicaciones híbridas, Android e iOS. Sus intereses varían desde las últimas tendencias y blogs tecnológicos hasta leer, mirar televisión y disfrutar de todo tipo de música. Puede obtener más información sobre él en http://dev.yapr.org/carlosandreu.



Jeremy Nortey, Software Developer, IBM

Jeremy Nortey es un desarrollador de software de la IBM Mobile Foundation dentro del Grupo de Software. Desarrolla software y garantiza la calidad de soluciones móviles. Se especializa en iOS y a veces trabaja en la construcción de aplicaciones nativas para iPhone en su tiempo libre. Sus pasatiempos incluyen el fútbol sóccer y correr.



Raj Balasubramanian, Consulting IT Architect, IBM Mobile Foundation, IBM

Raj Balasubramanian es un arquitecto de productos del Grupo de Software de IBM que trabaja en la IBM Mobile Foundation. Lidera la interacción entre servicios y clientes para Worklight de IBM y para la IBM Mobile Foundation. Antes de su rol de desarrollador, lideró los proyectos relacionados con infraestructura y aplicaciones de proporción de compromisos para clientes relacionados con tecnologías SOA, BPM. Web 2.0 y Portal. Se interesa por todo lo técnico, historia, matemáticas y física. Actualmente, se encuentra en camino hacia su doctorado en la Universidad de Texas en Austin. Puede leer sobre sus aventuras técnicas y personales del pasado en su blog personal Gurukulam en http://balasubramanians.com/blog.



28-01-2013

Introducción

IBM Worklight, parte de la IBM Mobile Foundation provee una robusta plataforma para desarrollar rápidamente aplicaciones móviles, mientras que nivela las tecnologías basadas en la web que pueden ejecutarse en plataformas de dispositivos múltiples. Este artículo continúa a donde dejó la Parte 1 en el proceso de desarrollo de una aplicación móvil completamente funcional, autónoma, llamada "Todo", que le permite a un usuario de un móvil desarrollar y mantener una lista de tareas por hacer. En el proceso, aprenderá acerca de la memoria caché encriptada sin conexión, una medida de seguridad del cliente ejecutable Worklight que protege información confidencial de ataques de malware y robo de dispositivo.

Obtenga Worklight ahora

Descargue ya IBM Worklight Developer Edition 5.0 sin costos y sin fecha de vencimiento.

Hasta ahora ya debería haber configurado el IBM Worklight Studio en Eclipse IDE y debería saber cómo disparar un simple tipo de aplicación "Hola mundo" en iOS y en Android. Comenzaremos con la aplicación Todo que comenzó a desarrollar en la Parte 1. Descargue la aplicación inicial que creó en la Parte 1 e impórtela a su entorno Worklight Studio. (Como alternativa puede descargar los archivos del proyecto de aplicación Todo incluido en este artículo.)


Cómo desarrollar la aplicación

Cómo utilizar la aplicación de muestra

La aplicación de muestra que se describe aquí es presentada sólo como un ejercicio de ejemplo. Debido a que la transferencia masiva de archivos de la lógica de la aplicación se ubicará en JavaScript™ (específicamente utilizando JQuery), se destacarán las mejores prácticas en cuanto a la estructura lógica de la aplicación que obtenga una mejor legibilidad, confiabilidad y consistencia, como también se hará con las prácticas relacionadas con el uso del espacio de nombre y más.

La Figura 1 ilustra el flujo general del usuario ejecutando la aplicación móvil Todo. Para resumir, el usuario debería:

  1. Abrir la aplicación
  2. Ingresar una clave para almacenar los datos de forma segura y presionar Start para proceder al segundo panel.
  3. Ingresar cualquier texto que represente un nuevo elemento “para hacer" en el primer campo, luego agregarlo a la lista presionando el botón Agregar elemento.
  4. Marcar el elemento como "finalizado" presionando el elemento en la lista y seleccionándolo. Presionar Quitar finalizado para eliminar todos los elementos marcados como finalizados.
  5. Filtrar los elementos mostrados en el panel escribiendo el nombre completo o parte de este dentro del campo del segundo texto, luego presionar Filtrar elementos...
Figura 1. Paneles de aplicaciones de muestra
Figure 1. Sample app panels

Su primer paso para agregar esta funcionalidad a su aplicación es dividir su código en módulos que poseen responsabilidades específicas dentro de la aplicación. Todo tendrá estos módulos (Figura 2):

  • Módulo Constant obtiene y configura varias constantes que utilizará en su aplicación.
  • Módulo List es responsable de almacenar la lista para la sesión actual y gestionar eventos, como por ejemplo agregar un nuevo elemento a la lista, marcar los elementos como finalizados y remover los elementos que se encuentran como finalizados.
  • Módulo Vault se encuentra a cargo de encriptar y descifrar la lista. Aquí es donde nivelará la característica de Worklight, memoria caché encriptada sin conexión.
Figura 2. Módulos y sus interacciones
Figure 2. Modules and their interactions

Para estructurar su código, sacará provecho del común y popular Module Pattern, desde el mundo de JavaScript (vea los Recursos). El Listado 1 muestra el esqueleto de un módulo. Usted asigna una función auto ejecutable (o función inmediata) a un objeto (Módulo 1), el cual es contenido en su espacio del nombre (MYAPP). Es una buena idea pasar cosas como el objeto global (ventana, para la mayoría de los clientes de JavaScript) y bibliotecas de proveedores (como JQuery, para que pueda utilizar $ dentro de su módulo, aún si algo más utiliza $ fuera del módulo como Prototype.js u otra biblioteca). También es una buena práctica enumerar las instalaciones y asignar variables locales a estas porque es más rápido acceder variables locales que buscar pares de valor clave.

Por ejemplo:

var eoc = WL.EncryptedCache; eoc.open()

es mejor que hacer que JavaScript busque el objeto WL y luego busque la clave EncryptedCache que apunta hacia otro objeto que contiene la función abierta a la que desea llamar, cada vez que necesita llamar a esa función dentro de uno de sus módulos.

Listado 1. Esqueleto patrón del módulo
MYAPP.Module1 = (function (global, $) { 
//List dependencies: jQuery 1.7.2

//List private variables

//List private functions
var _init = function () {
console.log("I'm ready!");
}

//List one time initialization procedures 

//public API
return {
init : _init
};

}(window, jQuery)); //MYAPP.Module1

Este es un buen momento para mencionar algunas convenciones de estilo. Los caracteres en mayúscula se utilizan en estos códigos de muestra para constantes y los caracteres subrayados son utilizados delante de las funciones privadas para distinguirlas de las variables privadas. Puede extender esta convención para sacar provecho del “patrón de instancia única" en el que una única variable (var) es declarada por encima de cualquier función que requiera variables. Esto se hace debido a que JavaScript "eleva" las variables hacia la cima cuando se ejecuta el código (Lista 2) y debido a que a veces utilizar var produce resultados indeseables como sobrescribir una variable con el mismo nombre.

Listado 2. Ejemplo de elevación
//Example of Hoisting 
myname = "global";

function func() {
alert(myname); //returns: "undefined" 
var myname = "local";
alert(myname); //returns: "local"

Existe una construcción subyacente llamada closure, que les da poder a los módulos (vea los Recursos). En JavaScript, cierre se refiere a la variable local de una función que es mantenida con vida luego de que la función regresó. Esto tiene mucho que ver con el alcance en JavaScript que es definido por la función. Por ejemplo, el Listado 1 muestra un ejemplo de cierre en acción. Aquí, _init es una función privada sin acceso fuera de la función auto ejecutable asignada MYAPP.Module1, pero devuelve un objeto que tiene una referencia a esa función privada. De esta forma, puede acceder a esta fácilmente como MYAPP.Module1.init(). Esto puede lograrse debido al cierre.

JavaScript es un lenguaje de programación orientado hacia los eventos, semejante al patrón Observer descripto en casi cualquier libro de patrones de programación. En los scripts de JavaScript, los eventos son disparados cada vez que el usuario se desliza o hace clic sobre una etiqueta HTML o pasa el ratón sobre un enlace, para mencionar algunos ejemplos. Los eventos pueden ser oídos y puede disparar sus propios eventos.

Separación de intereses es otra práctica importante a seguir, la cual significa mantener separados el margen de ganancia, estilo y aplicación de la lógica. Su código HTML debería describir solamente la estructura de su aplicación; en HTML las funciones en línea de JavaScript no son recomendadas. Puede lograr esto utilizando el estilo de codificación orientado hacia los eventos, en el que el elemento específico que necesitaría tener la llamada en línea es en su lugar el observado para el evento específico que disparará la acción necesaria. Esto le permite tener la lógica de aplicación en un solo lugar, es JavaScript y no en múltiples lugares a través de HTML. Del mismo modo, debería evitar escribir HTML como parte de JavaScript y en su lugar utilizar plantillas (vea los Recursos).


Módulo constante

Comience por definir su espacio para el nombre (Listado 3). Para la muestra Todo app, nombre a su espacio para el nombre "TD". El espacio del nombre es básicamente un objeto que contiene otros objetos, todos relacionados con su aplicación. Es posible que a medida que su aplicación crece, TD ya haya sido definida en el espacio de nombre global. Utilice el patrón de var única para crear un objeto TD si aún no hay uno disponible. Esta función espacio de nombre asegurará consistencia en cuanto a la invocación al método. La invocación TD.namespace(TD.module1) agregará un objeto llamado módulo1 a TD. Observará cómo se puede declarar el espacio de nombre TD y utilizarlo para propagar los objetos debajo del espacio de nombre TD según lo necesite.

Listado 3. Función Namespace
/**********************************************************************************
* TD NAMESPACE
**********************************************************************************/
var TD = TD || {};

TD.namespace = function (ns_string) { 
var parts = ns_string.split('.'),
parent = TD, 
i;

// strip redundant leading global 
if (parts[0] === "TD") {
parts = parts.slice(1); 
}

for (i = 0; i < parts.length; i += 1) {
// create a property if it doesn't exist
if (typeof parent[parts[i]] === "undefined") {
parent[parts[i]] = {}; 
}
parent = parent[parts[i]]; 
}

return parent; 
};

En la Figura 3, puede ver que TD.Constant (su módulo constante) no posee ningún miembro privado. Sólo devolverá valores a través de lo que llamaremos Public API (es una clave simple de pares de valores creada mediante un objeto literal). Del mismo modo, la clave de elementos Todo es utilizada para la memoria caché encriptada sin conexión. Los valores encrypt y decrypt son simples valores booleanos que eventualmente le informarán a su módulo de bóveda si desea encriptar o descifrar.

Figura 3. Elementos del módulo
Figure 3. Modules and their interactions

Lista del módulo

Como puede apreciar en la Figura 1 y el Listado 4, el segundo panel de la aplicación es esencialmente la página principal de la aplicación. Contiene:

  • Un logo en el encabezado.
  • Un campo de texto (id="new-todo") que será utilizado para agregar elementos nuevos.
  • Un botón para agregar elementos nuevos (id="add").
  • Un botón para eliminar todos los elementos con marca de finalizado (id="delete").
  • Una lista desordenada para adjuntar la lista de elementos (id="todo-list").
  • Una etiqueta div (id="no_items") para mostrar el texto cuando la lista esté vacía.

Además nótese que agregando una propiedad data-filter=’true’ a su etiqueta <ul>, obtiene filtros de datos en forma gratuita (vea los Recursos). (http://jquerymobile.com/demos/1.1.0/docs/lists/index.html).

Listado 4. Panel principal HTML
<!-------------------------- MAIN PAGE  -------------------------->
<div data-role="page" id="main" >

<!-- <div data-role="header"><h1>Todo App</h1></div> -->
<img class="centerimg" src="images/logonobg.png"/>

<div data-role="content" >

<input type="text" name="new-todo" id="new-todo" 
placeholder="Write your tasks here..." />

<div class="controls" data-role="controlgroup" data-type="horizontal">
<a href="#" id="add" data-role="button" data-icon="plus">Add New</a> 
<a href="#" id="delete" data-role="button" data-icon="delete">Delete Done</a>
</div>

<ul id="todo-list" data-role="listview" data-filter="true" data-inset="true" >
</ul><!-- /todo-list -->

<div class="center" id="no_items"></div>

</div>
<!-- /content -->
</div>
<!-- /page -->

Para su lista de tareas, almacenará elementos en un lista de array. Por ejemplo:

[{content: “myTodo Item 1", done: false},
{content: “myTodo Item 2", done: true}].

Es sólo un array simple con pares de valores clave (objetos JavaScript) que tienen una cadena para el nombre de los elementos y un valor booleano para monitorear si un elemento ha sido finalizado o no.

Como se puede ver en la Figura 3, TD.List (su módulo de lista) contiene un método privado definido (_encrypt) que encriptará la lista (un array privado llamado "lista") que llama al método de encriptación de su módulo de bóveda desde su API público. También tiene la _item_template, que es una función generada desde una plantilla para mostrar elementos definidos dentro de su documento HTML; necesita sobrepasar un objeto que contenga el "elemento para la clave" y la lista de array.

Un ejercicio

Como ejercicio, quizá desee realizar un chequeo más elaborado (por ejemplo, verificar si item.done devuelve un Booleano, verificar valores nulos, etc.).

La función privada _add verificará si item.content no se encuentra vacío o indefinido y si recibe alguna respuesta cuando llama a un elemento.finalizado. Recuerde que las variables que no han sido asignadas con un valor predeterminado aparecerán como "undefined".

Listado 5. Función _add
_add =  function (item) {
if (item.content !== 'undefined' && item.done !== 'undefined' && item.content.length > 0)
{
list.push(item);
}
},// add item

La función _refresh_list ejecuta de forma dinámica una función de mapa en la lista de array y agrega un nuevo elemento a cada objeto almacenado en ese array. El resto de la función limpia la lista, adjunta el resultado de la plantilla generada basándose en la lista de la lista desordenada (ul) en el HTML y actualiza la vista. Este último método de llamada es jQuery Mobile y se utiliza específicamente para actualizar el estilo de la lista.

Listado 6. Función _refresh_list
_refresh_list = function () {

$.map(list, function (item, i) {
item.index = i;
});

list_ul.empty();
list_ul.append(_item_template({item : list}));
list_ul.listview('refresh');

if (list.length < 1) {
no_items.text('No items.');
} else {
no_items.text('');
}

}; // refresh list

Otro ejercicio

Quizá quiera cambiar este enfoque utilizando una estructura de datos diferente y un algoritmo diferente para generar y monitorear los ID. La meta es no necesitar el uso de la función mapa para repetir toda la lista y generar ID y no necesitar limpiar y volver a adjuntar la lista completa cada vez que agrega o elimina un elemento.

Luego, adjuntará acciones a los eventos. Si conoce jQuery, podría utilizarlo para ver o utilizar llamadas como bind(), live() y delegate(). En este ejemplo, on() se utiliza exclusivamente después de jQuery 1.7.0 debido a que esos métodos combinados, en vivo y de delegación son sólo envoltorios para on(). Además, $(esto) es "almacenado en la memoria caché" en una variable local llamada $esto debido a que lo ideal es utilizar esa función jQuery lo menos posible y limitar la cantidad de veces que se sumerge en el DOM (módulo objeto de documento) porque es una operación muy costosa. Note que alternamos los valores booleanos que describen si el elemento aparece como finalizado o no y agrega o elimina una clase llamada "finalizado" definida en la hoja de estilo (archivo .css) Finalmente encripta() la lista que persistirá aunque no cierre de inmediato la aplicación después de hacer clic en un elemento para marcarlo como finalizado.

Listado 7. Anexos al evento dentro la TD.List
add_btn.on('click', function () {

_add({content : new_item.val(), 
done: false });

new_item.val('');
_refresh_list();

_encrypt();

}); // add btn

list_ul.on('click', 'li', function () {

var $this = $(this),
$item = $this.find('a'),
index = $item.attr('id'), 
current_item = list[index];

current_item.done = !(current_item.done);

if (current_item.done) {
$item.addClass('done');
} else {
$item.removeClass('done');
}

_encrypt();

}); // list item

delete_btn.on('click', function () { 
var i = list.length;

while (i--) {
if (list[i].done) {
list.remove(i);
}
}

_refresh_list();

_encrypt();

});// delete btn

Un tratamiento similar se aplica para eliminar el botón, código que puede ver en Worklight Studio.

El API público para su Módulo de listas es muy simple, puede obtener la lista actual con la que trabaja, configurar una nueva lista y trabajar con esa y actualizar el conjunto actual de listas. Puede llamar estos métodos invocando el espacio de nombre TD seguido del nombre del módulo y por último una de las claves del objeto devuelto por su módulo.

Por ejemplo:

TD.List.get(), TD.List.set([]), TD.List.refresh()

sería válido mientras que:

TD.List._refresh_list(), TD.List._add()

no lo sería, ya que esas funciones sólo pueden ser llamadas dentro del módulo (recuerde, las funciones crean alcance) o por miembros privilegiados (como el objeto devuelto dentro de su módulo).

Listado 8. API público de la TD.List
//public API
return {
get : function () {
return list;
},
set : function (new_list) {
list = new_list;
},
refresh : function () {
_refresh_list();
}
};

Módulo Vault

El módulo TD.Vault es una envoltura para la funcionalidad de memoria caché encriptada sin conexión de Worklight (EOC). En base al desarrollo del almacenamiento local en HTML5, EOC es un método para almacenar datos persistentes sin tener que utilizar cookies. EOC le brinda los métodos para abrir y cerrar y luego leer y escribir valores cuando abre. Los detalles de seguridad detrás de EOC se encuentran fuera del alcance de este artículo, pero en pocas palabras, EOC saca provecho de una clave de usuario para encriptar los datos. Por lo tanto, el código HTML que es relevante a este módulo en particular es una página (el primer panel en la Figura 1) que contiene:

  • Una imagen de encabezado con logo o nombre de aplicación.
  • Un campo para la clave (id=’passcode’).
  • Un botón (id=’send_passcode’) para ingresar la clave.
Listado 9. HTML para la página del código
<!-------------------------- PASSCODE PAGE  -------------------------->
<div data-role="page">

<img class="centerimg" src="images/logonobg.png"/>

<div data-role="content">

<div class="center" id="invalid_passcode"></div>

<ul data-role="listview" data-inset="true">
<li data-role="fieldcontain">
<label for="passcode">Enter Passcode:</label> 
<input type="text" name="passcode" id="passcode" value="" placeholder="Your passcode" />
</li>
</ul>

<a id="send_passcode" href="#" data-role="button" data-theme="a">Start</a>
</div>
<!-- /content -->

</div>
<!-- /page -->

Comience su código JavaScript pasando algunas variables globales a su función de auto ejecución como por ejemplo jQuery (redefinido como $) y WL (espacio para nombre de Worklight, redefinido como WL). Entonces "almacena en la memoria caché" sus dependencias asignándoles variables locales. Esto le asegura poder llamar de forma sencilla a wl.EncryptedCache.open (en lugar de tener que pasar el espacio de nombre WL como un parámetro al módulo) y luego almacena en la memoria caché wl.EncryptedCache en una variable local llamada eoc. Luego, las variables privadas, por ejemplo, KEY almacena la frase clave que ingresó el usuario y se guardan algunas llamadas DOM para el botón y campo claves y un div que muestra un mensaje de error si la clave no es válida (Listado 10).

Listado 10. Módulo TD.Vault, dependencias y variables privadas
TD.namespace('TD.Vault');
TD.Vault = (function ($, wl) { 
//dependencies
var eoc = wl.EncryptedCache,
log = wl.Logger,
list_obj = TD.List,
CONST = TD.Constant,

//private variables
KEY = "",
send_passcode_btn = $('#send_passcode'),
passcode_fld = $('#passcode'),
invalid_passcode_div = $('#invalid_passcode'),

Un ejercicio final

Quizá quiera verificar la documentación Worklight e implementar un manejo específico de los errores basándose en el código de estado que le trae la función de devolución de llamada onErrorHandler.

A continuación, tiene algunas variables privadas, como por ejemplo _error, que simplemente registra cuando recibe un error por una devolución de llamada. Los métodos _setData y _getData especifican cómo obtiene y configura los datos que va a encriptar y descifrar (Listado 11).

Listado 11. Funciones _error, _setData y _getData
//private functions
_error = function () {
log.debug("error");
},

_setData = function (new_list) {
if (new_list) {
list_obj.set(new_list);
list_obj.refresh();
} 
},

_getData = function () {
return list_obj.get();
},

La definición del evento es similar a la de TD.List, a donde adjunta send_passcode_btn on click, que simplemente asigna la clave que el usuario ingresó cuando inició la aplicación para la variable privada KEY si la extensión ingresada es de al menos un caracter (Listado 12).

Listado 12. Evento adjunto para el botón de envío de clave
send_passcode_btn.on('click', function () {

var passcode = passcode_fld.val();

if (passcode.length > 0) {
KEY = passcode;

_decrypt();

$.mobile.changePage("#main", { transition: CONST.DEFAULT_TRANSITION });

} else {
passcode_fld.val('');
invalid_passcode_div.text('Invalid Passcode.');
}

});

A continuación, descifra los contenidos EOC llamando a la variable privada _decrypt(). Estas llamadas _open con una constante (CONST.DECRYPT) que indica que descifrará luego de que abra la memoria caché encriptada. La _función abierta dispara el evento eoc-open y envía la acción (decrypt) a _read(). Entonces tratará de leer la caché encriptada con la clave provista por el usuario. A esta altura llama _setData con los datos que obtuvo analizando un array en JavaScript con parseJSON (ese array contiene un par de valores que describen un sólo elemento o un índice del array). Si obtuvo algo desde la caché, su conjunto de llamadas enciende el módulo de lista para configurar la lista que obtuvo como la nueva lista. Finalmente, la lista de elementos HTML es actualizada llamando a la actualización desde el API público de la TD.List.

Listado 13. Funciones _close, _write, _read, _encrypt, _decrypt y un evento de escucha ‘eoc-open’
_close = function () {
var onCompleteHandler = function () { 
$.publish('eoc-closed'); 
};

//function(onCompleteHandler, onErrorHandler)
eoc.close(onCompleteHandler, _error);
},

_write = function () {
var data = JSON.stringify(_getData());

//function(key, data, onCompleteHandler, onErrorHandler)
eoc.write(CONST.TODO_ITEMS_KEY, data, _close, _error); 
},

_read = function () {
var onCompleteHandler = function (data) {
_setData($.parseJSON(data)); 
_close(); 
};

//function(key, onCompleteHandler, onErrorHandler)
eoc.read(CONST.TODO_ITEMS_KEY, onCompleteHandler, _error);
},

_encrypt = function () {
_open(CONST.ENCRYPT);
},

_decrypt = function () {
_open(CONST.DECRYPT);
};

$.subscribe("eoc-open", function (e, action) {
if (action) { // == CONST.ENCRYPT
_write();
} else { // == CONST.DECRYPT
_read();
}
});

Encriptar sigue casi los mismos pasos, excepto la acción que toma luego de abrir la caché, que llamará _write en vez de _read. Entonces ejecuta _getData para obtener la lista actual, convertirla en una sola cadena con JSON.stringify y almacenarla en la memoria caché. Cierre la caché cada vez que termine de encriptar y descifrar.

Las Figuras 4 y 5 muestran la aplicación terminada ejecutándose en el simulador de iPhone.

Figura 4. La aplicación terminada ejecutándose en el simulador de iPhone (Página de la clave)
Figure 4. The finished application running on the iPhone simulator (Passcode Page)
Figura 5. La aplicación terminada ejecutándose en el simulador de iPhone (Página principal)
Figure 5. The finished application running on the iPhone simulator (Main Page)

Conclusión

La Parte 2 de esta serie que nos presenta Worklight de IBM continúa sacándole provecho al entorno de la configuración de desarrollo Worklight en la Parte 1 y agrega funcionalidad para desarrollar la aplicación de muestra Todo. En el camino, aprendió cómo estructurar su código puede beneficiar el desarrollo, presentación y mantenimiento de su aplicación. También aprendió acerca de datos persistentes (lista de tareas) en su dispositivo sacando ventaja de la funcionalidad de la memoria caché encriptada sin conexión de Worklight. La conclusión de esta serie es que agregará conectividad al servidor con adaptadores para completar la aplicación de muestra.


Descargar

DescripciónNombretamaño
Sample application project filestodo-app-part2.zip18.5MB

Recursos

Aprender

Obtener los productos y tecnologías

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=Desarrollo móvil, WebSphere
ArticleID=856390
ArticleTitle=Trabajar con Worklight, Parte 2: Cómo desarrollar módulos estructurados y utilizar la característica de memoria caché encriptada sin conexión en IBM Worklight
publish-date=01282013