Posts Tagged ‘iOS’


Termina la primera conferencia de Apple en el 2016 y la novedad es iPad Pro.

Con un tamaño mas pequeño que el original y con las mejoras en la pantalla, y todo eso con las funciones agregadas en iOS, hacen de este pequeño tablet, el mas avanzado y potente en Apple. Todo bien hasta que Tim Cook dice que esto es el futuro de la computación personal. 

El tema es que para muchos la computación personal es una laptop con un sistema operativo “completo” como Windows 10 o MacOSX y por eso invertimos algo de dinero en equipos que tengan suficiente poder para soportar el paso del tiempo. Pero si sale un grande como Apple a decir que la computación personal no va por ahí, se hace todo muy confuso sobre todo porque el perjudicado no será el usuario final que ya hizo la inversión en una laptop, sino en el mismo Apple que hace cada vez más difícil vender una laptop considerando que ya dijo el mismo Tim Cook que no piensan en un MacBook con pantalla táctil.

Es bien fácil, ¿Para qué invertir más dinero en una laptop si con un iPad Pro tienes casi todo lo que necesitas y incluso algo más? Obvio que esto no funciona si eres un programador, pero para el resto de usuarios es una pregunta muy válida.

Si vamos a Apple.com una laptop MacBook Pro de 13 con retina display cuesta alrededor de 1600 USD, mientras que ir por un iPad Pro con todos sus accesorios va por el orden de los 1400 USD. Pero el ahorro disminuye si es que eliges las líneas nuevas como Air pero se vuelve casi equivalente si vas por la nueva MacBook donde el modelo “serio” está 1600 USD. De todo esto se desprende una cosa: Si realmente necesitas poder, vas por un MacBook Pro y ahorras dinero, pero si prefieres el look y las cosas básicas, tienes hasta 3 opciones: iPad Pro o Macbook o Macbook Air, casi por el mismo precio, y según Tim Cook iPad Pro es la elección para asegurar el futuro.

Mi sugerencia ya está hecha: Si quieres poder, ahorras dinero con una MacBook Pro. Si eres básico: puedes elegir por iPad Pro, MacBook o MacBook Air en ese orden.

Advertisements

Me gustan los comics, pero más me gusta coleccionarlos. Es decir, tener la certeza de que tengo las series completas y verificar la secuencia de los mismos: no hay nada mas satisfactorio que saber que completaste una serie. En el mundo Cómic muchos apuestan por el tema digital, pero creo que el cómic impreso tiene para largo, porque la sensación es distinta al del libro, quizá sea porque en un cómic hay mas tinta y sangre puesta.

Desde que Peru21 empezó a publicar su serie de cómics a precio accesible, empecé con este hobbie al punto que puedo decir que tengo todas sus publicaciones. Otras editoriales empezaron como Editorial VUK, Skull Editores, Med Comics y después de todos estos años puedo decir que he pasado por la experiencia completa del coleccionista. Es por eso que me puse a trabajar en una aplicación que ya está lanzada el día de hoy: SuperComics como una forma de facilitar la experiencia del coleccionista.

¿Cuál es la experiencia del coleccionista? Pues en estos puntos los resumo:

  • Cronograma: Si sabes cuando van a publicarse tus cómics, se hace más fácil estar al día. Es tan crítico, porque de lo contrario tendrías que estar dando vueltas a la tienda o al kiosco a cada rato y si bien hay ahora algunas tiendas especializadas en cómics, no es fácil darse un tiempo para visitarlas. Con un cronograma se puede controlar cuáles cómics ya tienes y cuáles te faltan.
  • Inventario: Debes poder llevar un control de los cómics que ya tienes. La clásica es que te compras el cómic en un kiosco, y por ahí que caíste por el centro comercial arenales y te diste una vuelta por las tiendas de cómics, viste una carátula interesante y te la compras para darte cuenta en tu casa de que ya la tenías. Llevar la cuenta es clave.
  • Lista de compras: A pesar de los cronogramas, siempre sucede de que te faltó comprar un cómic, o que por esas cosas, perdiste alguna y necesitas reponer así que tendrás que ir a la tienda, kiosco, feria o evento donde esté la editorial. Tendrás que ir apuntando correctamente cuáles son los cómics que te faltan en una lista, aunque para evitar confusiones, tienes que llevar la carátula.
  • Tiendas: como ya dije hay algunas tiendas de cómics que van surgiendo, y algunos kioscos se han comprado el pleito de ofrecer cómics. Personalmente, hay una señora en Jesús María que incluso me guarda los cómics para no perder ninguno, y cruzando la calle hay otra tienda super caleta que te venden el mismo cómic con tu bolsicartón gratis y en bolsita, super cool. Lo malo es que si no conoces, estos lugares te los pasas sin darte cuenta. Otro ejemplo era la tienda de Perú 21 en el centro de Lima, donde podías comprar los cómics atrasados y encima el encargado te regalaba los posters de promoción, hasta ahora los tengo en su tubo porque ya me falta pared para colgarlos.

Finalmente, falta una que es el intercambio, porque si eres coleccionista, seguro que tienes repetidos que puedes cambiar o vender.

En la versión 2 de mi aplicación ofrezco las 4 primeras experiencias de tal forma que sea más fácil empezar a coleccionar, o para los que ya llevan algo de tiempo, controlar su colección más fácilmente.

La prioridad es saber lo que hay en este momento publicado y que ya se puede comprar. Por eso la pantalla de inicio comienza justamente con esos cómics:

screen01

screen14

Los 10 últimos cómics publicados se podrán ver en un carrusel, y si haces click en la portada verás todos los datos del mismo, así como la carátula completa.

screen01 screen09

Puedes ver el logo y el nombre de la colección, alguna nota importante, luego la carátula completa y si deslizas hacia abajo verás la información del cómic y sobre todo 3 botones:

  • Nola: marcas este botón si ya tienes el cómic, de tal forma que cuando regreses, podrás ver que ya lo tienes, porque habrá cambiado a “Yala”, como hacías con las figuritas de tu álbum.
  • Comprar: Si es que quieres comprar este cómic, al marcar este botón se agregará a tu lista de compras, de tal forma que los tendrás a la mano cuando vayas a la tienda. El botón se cambiará a “Lista” cuando hayas agregado el cómic.
  • Social: Podrás compartir orgulloso la carátula del cómic y sacarle pica a tus amigos de que ya lo tienes o que te gustó. Este botón te dará la opción de compartir la carátula en las redes sociales que tengas instalado en tu equipo.

Desde aquí, podrás ir casi a donde quieras:

  • Regresar al inicio: Para ver nuevamente el carrusel de comics recién publicados
  • ¿Que otros títulos tiene esta serie?: Al costado del nombre de la colección hay un punto blanco con una flecha hacia arriba. Haciendo click ahí, podrás ver la lista completa de títulos en esa seríe, al menos los que ya han sido anunciados. En algunos casos, la editorial publica un cronograma completo y podrás ver la serie completa con fechas de publicación y todo. Algo como esto:

screen15

Donde podrás ordenarlos o buscar en los títulos. El botón blanco con la flecha esta vez te llevará hacia la pantalla de la editorial:

screen06

Donde verás las otras series publicadas por esta casa, también con la opción de ordenar y buscar.

Ahora prestemos atención a los 4 botones de la parte de abajo:

  • Novedades: Es una sección secreta del app donde estarán los cómics últimos registrados. O sea que puede que aún no estén disponibles en tiendas. Si la editorial la ha anunciado con anticipación, la encontrarás en esta sección.

screen13

  • Catálogo: Encontrarás otro carrusel con los logos de todas las editoriales que publican localmente, ya sea directamente o a través de algún socio. Es el caso de editoriales gringas como Marvel, Dc, Image, Dark Horse, donde sus títulos son publicados por editoriales locales. Igual, si por ejemplo quieres ver donde está “The Walking Dead” la podrás encontrar tanto en Image como en Editorial Vuk, lo mismo para los otros títulos.

screen04 screen05 screen16

  • Compras: Si es que le diste a comprar en algún cómic, aquí es donde podrás ver tu lista de compras. De esta manera no vas a necesitar llevar tu papelito o acordarte de memoria cual querías comprarte. Después que te compres el cómic, no olvides de desmarcarlo para mantener tu lista de compras ordenada.

screen17

  • Tiendas: ¿Quieres saber donde comprar tus cómics? Pues en esta sección verás algunas de las tiendas que más se dedican al tema, y por supuesto, también he puesto el kiosco en Jesús María donde siempre compro los mios. Personalmente les recomiendo este lugar porque suele tener muchos de los números antiguos sobre todo los de Peru21. Podrán ver las tiendas en una lista, pero también en un mapa.

screen11 screen12

Inicialmente, sólo estaban incluidos cómics o “grapa” como se le dice en la jerga, pero gracias a la amabilidad de Skull Editores, ya se ha incluido su colección de Spawm que es un TPB con 4 números en cada volúmen. En la medida que reciba la información de las demás editoriales, las estaré incluyendo.

Y con esto terminamos las funciones que se pueden ver, ahora veamos las funciones que no se pueden ver:

  • Actualización automática: Salió un nuevo cómic o una editorial acaba de anunciar una nueva serie?. No te preocupes que la aplicación se actualizará automáticamente, sin que te pregunte ni nada.
  • Uso de datos: Ok, actualizar automático es cool, pero consume el saldo si no hay wifi. No te preocupes. Cuando descargues el app, viene incluida la última versión de la base de datos, lo que significa que descargarás solamente la nueva información, la cual es de mas o menos 24K por cómic. Saca tu línea: Si se publican 10 cómics en un día, recibirás una actualización de aprox 240 a 260K, casi nada. Además, si es que te quedaste sin saldo, igual podrás utilizar el app, y hacer de todo, menos el mapa que es la única función en línea. La opción Social también funcionará siempre con Whatsapp, ya que se puede usar sin saldo, Facebook te dirá que no puede subir la foto y las demás si te dirán que no tienes red.

En el futuro se vienen cosas interesantes:

  • Backup de la base de datos: Imagina que ya repasaste todo el catálogo y le diste Yala a todo. Sería una pena perder todo eso porque cambiaste de equipo. Pronto podrás hacer un backup de eso y tenerlo disponible para seguir controlando tu colección.
  • Integración con Facebook: Podrás compartir mas cosas en Facebook desde el mismo app.Además, con esto te podrás identificar de tal forma que puedas recuperar el backup de tu colección.
  • Intercambio/Venta de cómics: ¿Tienes cómics repetidos? ¿Te falta algún cómic? Podrás publicarlo y conectarte con los demás que estén en la misma situación.

La publicación en Google Play Store debe estar en unas dos semanas lista o antes. Con Apple, esto puede tomar algo más de tiempo. Con Windows Store para WP8, voy a demorar un poco más pues Microsoft está en transición así que esa versión me está demorando y si sale esa, podría también estar la versión para Windows 10.

Comentarios y sugerencias en este post, o también al correo del app en supercomics@supermio.com.pe


Tengo un HP Stream 7 que la compré por menos de 100 USD y corriendo Windows 8.1. No era Windows 8.1 RT, era el mismo Windows 8.1 que tenia en mi laptop en ese momento. Bueno, una versión reducida llamada Windows 8.1 with Bing, pero para todos los efectos, era lo mismo. Significaba que a mi tablet le faltaba el teclado y el mouse para ser mi version pobre de una surface. Y lo hice gracias a un cable OTG y un teclado Logitech K-400 que tiene un trackpad integrado.

Hoy le hice una prueba de batería y teniéndola completamente cargada en la mañana, llega al final del día con batería suficiente como para no apagarse, por lo que pasa la prueba.

El rendimiento es como tiene que ser, nada espectacular, pero tampoco es para quejarse, es tan rápida como cualquier tablet Android o iPad considerando que no hay comparación pues estamos hablando de un sistema operativo completo corriendo un office completo y todo eso contra versiones reducidas para acomodarse al hardware.

Recientemente la actualicé a Windows 10 Home y en lugar de ponerle el Office Completo, le puse la version Mobile que es mas que suficiente y todo muy bien.

Como era de esperarse, los programas disponibles dentro del Windows Store son pocos, pero al menos están las aplicaciones clave: Facebook, Twitter, Office, SlingPlayer. Todas estas son lo que llaman “Modern Apps” que se integran con las capacidades de Windows 10 de acomodarse en la pantalla y todo eso. Ahora, si te falta alguna app, puedes ponerle el que tienes en tu PC sin problema. La única restricción es que viene solamente con 32 Gb de disco, pero se puede ampliar mediante microSD.

Lo que hay que notar aquí es que hablamos de una buena alternativa para el trabajo fuera de la oficina que viene con una característica necesaria para el trabajo empresarial: encriptación de disco. Obviamente que no nos estaremos conectando al dominio de la empresa, pero eso importa poco, ya que podemos incluir esa autenticación en las aplicaciones.

Windows 10 le cae muy bien a esas tablets que desde el saque se muestran superiores a la competencia porque en realidad se vuelven en pequeñas laptops en todo sentido. Incluso HP tiene toda la línea Stream que incluye laptops por 200 USD o tablets de 8 pulgadas, algo que hay que revisar porque no son un juguete.


Esta es una continuación de la serie sobre desarrollo en móviles que comencé aquí. Les recomiendo comenzar a escribir el código fuente desde la parte 1 ya que no se publica el código para descarga.

En esta parte, haremos una introducción a lo que creo que es lo más potente que se ha incluido en Ionic, UI-Router. Hay otros componentes que hacen lo mismo, pero creo que lo más importante aquí es el concepto de estados.

Un estado es un conjunto de vista, controlador y modelo que hace una labor específica en nuestro programa. Tenemos que pensar en una labor muy simple para ver la potencia de este concepto. Por ejemplo, listar todos los correos que tenemos, es un estado, o ver un email en particular es otro estado. Mientras más simple que sea, más reutilizable será el estado.

Ahora, quizá me adelante, pero es posible que un conjunto de estados compartan una base común, por ejemplo, la lista de correos tienen que mostrarse dentro de una ventana con un menú de controles. Para eso, existe el concepto de estados abstractos.

Primero vamos por lo más simple, agregemos los estados. En nuestro caso, es simple pues tenemos solamente un estado que lista todas las noticias. Así que modificamos el archivo app.js para definir el estado.

angular.module('starter', ['ionic','controllers'])

.run(function($ionicPlatform,$rootScope) {
  $ionicPlatform.ready(function() {
    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
    // for form inputs)
    if(window.cordova && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
    }
    if(window.StatusBar) {
      StatusBar.styleDefault();
    }
  });
})
.config(function($stateProvider, $urlRouterProvider) {
  $stateProvider
    .state('news', {
      url: "/",
      views: {
          "home":{
              templateUrl: "templates/news.html",
              controller: "newsController as news"
          }
      }
  })
  // if none of the above states are matched, use this as the fallback
  $urlRouterProvider.otherwise('/');
});

Desde la línea 15 se puede ver la definición del estado. En nuestro caso, hemos llamado al estado con el nombre de “news” y le hemos asignado el url “/”, también se le ha asignado una vista llamada “home” que tiene un template llamado news.html en la carpeta templates y tiene asignado el controlador newsController al que le hemos puesto un alias news. ¿Qué significa todo esto?

Traducción: Al invocarse el estado “news”, AngularJS va a tomar el archivo news.html y le va a asignar el controlador newsController para ejecutarlo. Simple.

En la línea 27 se pone que la aplicación tomará la ruta “/” como ruta por defecto, lo que llevará a la aplicación al estado “news”.

Veamos el archivo news.html.

<ion-view view-title="Noticias">
 <ion-header-bar class="bar-stable">
 <h1 class="title">Ionic Blank Starter>/h1>
 </ion-header-bar>
 <ion-content>
 <p> Hay {{notas.length}} noticias</p>
 <div class="list"
 ng-repeat="noticia in notas">
 <div class="card">
 <div class="item item-divider">
 {{noticia.doc.fecha}}</br>{{noticia.doc.titular}}
 </div>
 <div class="item item-text-wrap">
 <b>{{noticia.doc.resumen}}</b>
 </div>
 <div class="item item-divider">
 Autor: {{noticia.doc.autor}}
 </div>
 </div>
 </div>
 </ion-content>
</ion-view>

El contenido es casi todo lo que antes había en el archivo index.html pero con las tags iniciales en lugar de las <ion-content< que habían antes. Esto le indica a AngularJS que estamos definiendo una vista.

Lo que hemos hecho es separar la vista y asignarle un controlador de forma programática lo cual es muy conveniente para poder reutilizar el código.

Ahora, para que todo esto funcione falta indicarle a AngularJS donde vamos a mostrar el resultado del estado, y para eso veamos como queda el archivo “index.html”

<!DOCTYPE html>
<html>
 <head>
 <meta charset="utf-8">
 <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
 <title></title>
 <!-- compiled css output -->
 <link href="css/ionic.app.css" rel="stylesheet">
 <!-- ionic/angularjs js -->
 <script src="lib/ionic/js/ionic.bundle.js"></script>
 <!-- cordova script (this will be a 404 during development) -->
 <script src="cordova.js"></script>
 <!-- your app's js -->
 <script src="js/services.js"></script>
 <script src="js/controllers.js"></script>
 <script src="js/app.js"></script>
 <script src="lib/pouchdb/dist/pouchdb.js"></script>
 </head>
 <body ng-app="starter">
 <ion-nav-view name="home"</ion-view>
 </body>
</html>

En la línea 20 notarán que hemos reemplazado toda la parte que mostraba las noticias por el tag . Aquí le estamos diciendo a AngularJS que queremos que se muestre la vista llamada “home”, que la hemos definido antes en el archivo app.js.

Traducción, AngularJS cargará news.html, le asignará el controlador newsController y la mostrará en el tag denominado “home”.

¿Cuál es la ventaja de todo eso? la respuesta es separación de responsabilidades. El diseñador Web podrá trabajar en la parte de la presentación con el html y el CSS y colores y demás, mientras que el programador podrá trabajar en el controlador, y si por ahí hay nuevas versiones de la vista o el controlador, pues simplemente se cambia en la definición de la vista y nuestra aplicación no sufrirá el cambio.

Ahora bien, regresemos a la pregunta inicial que recibí y que generó todos estos posts. La pregunta fue que si tenemos una lista de objetos de la base de datos, como hacemos para mostrar los detalles de estos objetos. Pues bien, en nuestro ejemplo, ya estamos recuperando todos los datos así que podríamos tener los detalles ocultos mostrando sólo las cabeceras. Para eso podemos usar la ayuda de Angular-bootstrap, así que instalemos rápidamente.

Paso 1: agregar el CSS en el index.html

<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">

Paso 2: instalar el componente angular-bootstrap con bower

bower install angular-bootstrap --save

Paso 3: agregar la referencia en el archivo index.html

<script src="lib/angular-bootstrap/ui-bootstrap-tpls.js"></script>

Con el angular-bootstrap listo, sólo nos queda modificar el template news.html para utilizar el componente accordion:

<ion-view view-title="Noticias">
 <ion-header-bar class="bar-stable">
 <h1 class="title">Ionic Blank Starter</h1>
 </ion-header-bar>
 <ion-content>
 <p> Hay {{notas.length}} noticias</p>
 <accordion close-others="oneAtATime">
 <accordion-group ng-repeat="noticia in notas"
 heading='{{noticia.doc.fecha}}-{{noticia.doc.titular}}'>
 <b>{{noticia.doc.resumen}}>/b>;
 </br>
 Autor: {{noticia.doc.autor}}
 </accordion-group>
 </accordion>
 </ion-content>
</ion-view>

Y obtendremos algo como esto, donde haciendo click en los títulos se despliega el contenido de la noticia:
gen21

Todo bien, pero hay otra alternativa, sobre todo si es que queremos ver los detalles de la noticia de forma mas flexible sobre todo si hay mucho texto, pues en ese caso deberíamos tener una lista de noticias y al seleccionar un titular, ir a otra pantalla para ver solamente esa noticia en toda la pantalla.

Para esto debemos crear otro estado a nuestra app, así que hagámoslo. En el archivo app.js agregamos ese nuevo estado y nuestro nuevo app.js lucirá así:

angular.module('starter', ['ionic','controllers','ui.bootstrap'])

.run(function($ionicPlatform,$rootScope) {
  $ionicPlatform.ready(function() {
    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
    // for form inputs)
    if(window.cordova && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
    }
    if(window.StatusBar) {
      StatusBar.styleDefault();
    }
  });
})
.config(function($stateProvider, $urlRouterProvider) {
    $stateProvider
        .state('news', {
        url: "/",
        views: {
            "home":{
                templateUrl: "templates/news.html",
                controller: "newsController as news"
            }
        }
    })
        .state('detail', {
        url: "/detail/:id",
        views: {
            "home":{
                templateUrl: "templates/detail.html",
                controller: "detailController as detail"
            }
        },
        resolve:{
            detail: function($stateParams){
                return $stateParams.id;
            }
        }
    })
    $urlRouterProvider.otherwise('/');
});

Los puntos de interés son la línea 27 donde fijamos el URL y también indicamos que el url vendrá con el parámetro ‘:id’; la línea 34, donde estamos fijando que la variable “detail” será pasada al controlador y tendrá el valor del parámetro ‘id’.
Ahora veamos como ha quedado el controlador nuevo en el archivo controller.js:

.controller('detailController',function($scope,db,detail){
	db.get(detail).then(function(doc){
        $scope.event=doc;
    })
})

Prestemos atención en la línea 11 que estamos pasando la variable ‘detail’ que definimos en la definición del estado y usamos ese valor para recuperar el objeto desde la base de datos. El valor del parámetro ‘id’ debe ser el id del documento que hemos elegido. La función db.get la estamos invocando desde el servicio que hemos definido en el archivo services.js así que tenemos que agregar este a la definición:

return {
  init: function(){
        db.replicate.from(remote,{live:true,retry:true})
        .on('paused',function(info){
            db.allDocs({startkey:'news_\uffff',endkey:'news_',descending:    true,include_docs:true})
            .then(function(result){
                  $rootScope.$broadcast('refrescar',result.rows);
            });
          });
        },
   get: function(id){
        return db.get(id);
   }
}

El documento resultante lo grabaremos en la variable de escope event para usarla en el template para mostrar.

Para verificar esto, veamos como quedan los templates.
Primero, index.html:

<!DOCTYPE html>
<html>
 <head>
 <meta charset="utf-8">
 <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
 <title></title>
 <!-- compiled css output -->
 <link href="css/ionic.app.css" rel="stylesheet">
 <link href="lib/angular-bootstrap/ui-bootstrap-csp.css" rel="stylesheet">
 <link href="css/bootstrap.css" rel="stylesheet">
 <!-- ionic/angularjs js -->
 <script src="lib/ionic/js/ionic.bundle.js"></script>
 <script src="lib/angular-bootstrap/ui-bootstrap-tpls.js"></script>
 <!-- cordova script (this will be a 404 during development) -->
 <script src="cordova.js"></script>
 <!-- your app's js -->
 <script src="js/services.js"></script>
 <script src="js/controllers.js"></script>
 <script src="js/app.js"></script>
 <script src="lib/pouchdb/dist/pouchdb.js"></script>
 </head>
 <body ng-app="starter">
 <ion-nav-bar class="bar-positive">
 <ion-nav-back-button class="button-clear">
 <i class="ion-arrow-left-c"></i> Regresar
 </ion-nav-back-button>
 </ion-nav-bar> 
 <ion-nav-view name="home"></ion-view>
 </body>
</html>

Aquí solamente hemos añadido una cabecera para facilitar la navegación en las líneas 23 a la 27.
Ahora veamos el archivo donde mostramos la lista de noticias: ‘news.html’

<ion-view view-title="Noticias">
 <ion-header-bar class="bar-stable">
 <h1 class="title">Noticias</h1>
 </ion-header-bar>
 <ion-content>
 <ion-list>
 <ion-item ng-repeat="noticia in notas" 
 ui-sref='detail({id:"{{noticia.id}}"})'>
 {{noticia.doc.fecha}}-{{noticia.doc.resumen}}</ion-item>
 </ion-list>
 </ion-content>
</ion-view>

Notaremos en las líneas 6 a la 10 que hemos incluido una lista y cada item tiene una directiva nueva: ui-sref que se encarga de llevarnos al estado que se indica cuando se hace click en el item; además le estamos pasando el parámetro id con el valor del id del documento.
Y ahora, para mostrar la noticia usamos el template detail.html que lucirá así:

<ion-view view-title="Noticia">
 <ion-header-bar class="bar-stable">
 <h1 class="title">Detalle</h1>
 </ion-header-bar>
 <ion-content>
 <div class="card">
 <div class="item-divider">
 <h2>{{event.fecha}} {{event.titular}}</h2>
 </div>
 <div class="item-body">
 {{event.resumen}}
 </div>
 <div class="item-divider">
 <h2>{{event.autor}}</h2>
 </div>
 </div>
 </ion-content>
</ion-view>

Como ven, los detalles del documento recuperado los leemos usando la variable ‘event’.

Y el resultado es una app con dos estados: news que muestra la lista de noticias disponibles y al hacer click en alguna llegamos al segundo estado llamado detail donde se muestra la noticia seleccionada completa, para lo cual recibe un parámetro que nos sirve para recuperar los datos desde la base de datos.

gen22

gen23

Seguramente notaremos que para el ejemplo que he desarrollado, hacer otra llamada a la base de datos puede parecer un desperdicio, pero lo que he tratado de hacer es mostrar como pasar parámetros y como utilizar los estados.Lo mas importante es que tenemos dos estados que podemos reutilizar en el resto de nuestra app.

NOTA: Parece que tengo un lector que ha sido responsable y ha seguido todos los pasos. Muchas gracias a Tercio Santos. Al parecer tiene un problema con el app y luego de pruebas en mi equipo, puedo decir que no hay problema con el código. Sólo por si acaso, recomiendo la instalación del plugin Cordova Whitelist:

ionic plugin add cordova-plugin-whitelist

Sucede que en las últimas versiones de Android, es necesario especificar los lugares donde el app puede ingresar y para eso se puede usar whitelist. Si es que no está, Android puede bloquear el acceso a internet de su app, por eso es mejor que esté aunque no lo utilicen todavía. Para una implementación en producción si es necesario configurar bien esos permisos. Ya llegaremos a eso.
Y para que vean que el código camina, aquí va un pantallazo. Recuerden, corre el app la primera vez con red, se muestran los datos, y listo, ya pueden ponerlo en modo avión si quieren y el app tendrá que mostrarle los datos.


Esta es una continuación de la serie sobre desarrollo en móviles que comencé aquí. Les recomiendo comenzar a escribir el código fuente desde la parte 1 ya que no se publica el código para descarga.

Ayer mencioné que iba a publicar un nuevo post para contestar una pregunta que recibí en la semana. Notarán el post sobre tips para rendimiento donde la moraleja resumida es que preocuparse por la velocidad de PouchDB contra Sqlite es detenerse en el arbol en lugar de apreciar todo el bosque.

La primera pregunta que recibí fue sobre como hacer para recuperar una lista de objectos y luego recuperar los detalles de un objeto en particular. Como en cualquier correo, tienen la lista de correos y cuando hacen click en uno en particular, verán el contenido en otra ventana. Este mecanismo es la perfecta introducción al concepto de estados que Ionic soporta mediante la inclusión de UI-Router.

Primero hagamos un poco de orden en el código que dejamos en la parte 2 de esta serie. Para los que recién llegan, no hay un download del código, así que vayan a la Parte 1 para que hagan el código paso a paso con el post.

Como podrán ver, tenemos el código todo regado en el archivo app.js lo cual esta mal. Lo que yo suelo hacer es tener un archivo para los controladores (controller), otro para los servicios (service, factory) y dejamos el app.js lo más limpio posible. Esperen!!, services y factories es algo que no había mencionado hasta ahora. Así que toca un poco de introducción.

Los que venimos del mundo de cliente/servidor y objetos y todo eso recordaremos que solíamos tener un super objeto casi global para ciertas tareas que estaba corriendo todo el tiempo esperando que le pidamos cosas. Si es que necesitábamos mantener la consistencia de sus operaciones, entonces declarábamos este objeto como un Singleton y listo, estabamos seguros que todas las operaciones las podíamos verificar antes de mandarlas. Pues eso en el mundo de AngularJS lo encontramos en las estructuras Service y Factory. ¿Cuál usar? Personalmente yo utilizo Factory la mayor parte del tiempo, pero Service también debería hacer el trabajo. La diferencia principal es que Factory es un Singleton y Service no, nada mas.

Comencemos por el mas fácil, pongamos todos los controladores en un módulo:

angular.module('controllers',[])
.controller('newsController',function($scope){
	$scope.notas=[];
	$scope.$on('refrescar',function(event,news){
		$scope.$apply(function(){
			$scope.notas = news;
		})
	})
})

Llamemos a este archivo controllers.js y pondremos aquí todos los demás controladores que nos hagan falta.

Ahora, para que el cambio sea completo, hay que cambiar tanto el app.js y el index.html.

angular.module('starter', ['ionic','controllers'])

Esta es la única línea que cambia del archivo app.js. Simplemente le estamos diciendo a AngularJS que el módulo App.js llamado ‘starter’, tiene una dependencia adicional llamada ‘controllers’.

<!DOCTYPE html>
<html>
 <head>
 <meta charset="utf-8">
 <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
 <title></title>
 <!-- compiled css output -->
 <link href="css/ionic.app.css" rel="stylesheet">
 <!-- ionic/angularjs js -->
 <script src="lib/ionic/js/ionic.bundle.js"></script>
 <!-- cordova script (this will be a 404 during development) -->
 <script src="cordova.js"></script>
 <!-- your app's js -->
 <script src="js/controllers.js"></script>
 <script src="js/app.js"></script>
 <script src="lib/pouchdb/dist/pouchdb.js"></script>
 </head>

En el caso de index.html, hemos agregado la línea 14 donde le indicamos que tiene que cargar el archivo controllers.js con nuestros controladores. Y listo, ya tenemos algo mas de orden.

Ahora nos toca ordenar la base de datos. Lo ideal sería tener un objeto que esté siempre disponible gestionando las llamadas a la base de datos, por lo que aplica a ser un Service o un Factory. Prefiero un factory como ya les había dicho así que esto quedaría en un módulo de la siguiente manera.

angular.module('services',[])
.factory('db',function($rootScope){
    var key = 'bentareadyessharyinessee';
	var pass = 'OnEixgKgpt8LyEtl0S5DkAon';
	var remote = 'https://'+key+':'+pass+'@supermio.cloudant.com/news';
	var db = new PouchDB('news');

    return {
        init: function(){
            db.replicate.from(remote,{live:true,retry:true})
                .on('paused',function(info){
                db.allDocs({startkey:'news_\uffff',endkey:'news_',descending: true,include_docs:true})
                    .then(function(result){
                    $rootScope.$broadcast('refrescar',result.rows);
                });
            });
        }
    }
})

Llamaremos services.js a este archivo y como podrán ver es un Factory que publica solamente un método llamado init. Este método tenemos que invocar para que se inicie el manejo de la base de datos. Si queremos simplificar nuestro archivo de controladores quedará así:

angular.module('controllers',['services'])
.controller('newsController',function($scope,db){
 db.init();
 $scope.notas=[];
 $scope.$on('refrescar',function(event,news){
 $scope.$apply(function(){
 $scope.notas = news;
 })
 })
})

Notarán que en la línea 1 se ha agregado el módulo services como dependencia y en la línea 3 estamos llamando al método init del Factory db.

Y el app.js quedará así:

angular.module('starter', ['ionic','controllers'])

.run(function($ionicPlatform,$rootScope) {
  $ionicPlatform.ready(function() {
    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
    // for form inputs)
    if(window.cordova && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
    }
    if(window.StatusBar) {
      StatusBar.styleDefault();
    }
  });
})

Comparado con el desorden que teníamos al principio ahora todo se ve más ordenado. Pero lo mas importante es que podemos centrarnos en escribir servicios o factories de forma general y reutilizarlos en otros proyectos simplemente copiando los archivos, lo mismo con controladores.

Una sugerencia final es que según estuve leyendo, esto de separar el código en controladores y servicios es bueno, pero no sirve si nuestro proyecto crece demasiado. La solución es la de hacer la separación por funciones de nuestro app y dentro de cada función poner los controladores y servicios separados en archivos. Esto lo veremos mejor cuando incluyamos el concepto de estados.

Continuaremos en otro Post porque el tema de estados es muy importante entender el concepto antes de programar.

NOTA: Soy muy flojo programando, así que hacer todo esto paso a paso lo hago para combatir este defecto. Así que para notar que este esfuerzo vale la pena, les pido que sigan el tutorial desde la parte 1, si les paso el código para que lo copien simplemente no vale ni mi esfuerzo ni ustedes tampoco van a entender.


En la semana he recibido un par de consultas que me parecen super importantes y que trataré de responder en un par de post y de pasada repasar los fundamentos.

La segunda pregunta que recibí creo que es mejor tratarla primero: ¿Ionic con PouchDB y Cloudant es más rápido que usar MySQL y websql? Veamos, desde mi punto de vista por más que parece lo mismo, no lo son.

Antes debemos revisar un par de puntos:

Primero, PouchDB es además una capa de abstracción de datos porque puede utilizar varios tipos de archivos para almacenar los datos (Adaptadores), principalmente puede usar IndexedDB, WebSQL y otros menos usados. La recomendación es que le indiquemos a PouchDB que utilice WebSQL si queremos que sea más rápido. En alguna eventualidad, PouchDB nos libra del problema pues puede elegir automáticamente cual es el mejor almacenamiento para nuestros datos.

Segundo, WebSQL, sqlite o como lo quieran llamar, es un estándar que ya está “depreciado”. El consorcio que maneja los estándares de Internet ya ha fijado que IndexedDB sea el estándar. Los navegadores siguen teniendo soporte para WebSQL pero no se sabe exactamente hasta cuando, mas bien IndexedDB es una obligación para todos.

Ahora comparemos:

PouchDB y Cloudant (o CouchDB en general) es realmente una solución de gestión de datos de extremo a extremo que parte del concepto denominado “offline first” donde la aplicación cliente, luego de una sincronización inicial, está preparada para trabajar sin conectarse al Servidor. Incluso, hay otro movimiento más radical denominada “No Backend”, pero no lleguemos a extremos, de alguna manera la base de datos es el backend y, si fuera necesario, se puede colocar una capa de lógica intermedia. Para resumir, PouchDB – CouchDB incluye, gestión de datos, sincronización y segmentación de datos. Un aspecto adicional es que, dado que no hay esquemas, es necesario “transformar” los datos del modelo relacional para que cuadre en el modelo “documental” de CouchDB y para esto pensaremos en la estructura que mejor nos acomode en la aplicación cliente lo que significa que vamos a “limpiar” el modelo para tener solamente lo que nos interesa.

MySQL y WebSQL (o sqlite para los amigos) es en el sentido estricto solamente un gestor de datos. Pasar datos desde MySQL a la base local en WebSQL es algo que tenemos que hacer nosotros, al igual de la segmentación de datos. Además, dado que en sqlite tenemos tablas y un dialecto de SQL, nuestra primera tentación será la de repetir el modelo de datos que tenemos en el backend.

Entonces, para comparar papas con papas, hablemos del aspecto de gestión de datos solamente de ambas soluciones. La verdad dura y simple es que consultar datos con sqlite es más rápido que consultar datos con PouchDB. Hay un documento que explica el rendimiento comparado de las bases de datos en el lado del cliente que pueden consultar Comparación datos móviles

Ahora bien, esto no me parece determinante porque, dependiendo del modelo de datos que tengamos, es muy probable que con el modelo relacional de sqlite tengamos que hacer más consultas que con PouchDB. Esto no tengo como sustentarlo en números, pero en los proyectos que he realizado es algo evidente que si modelas bien, consultas menos y dado que en un “Documento” de CouchDB puedes organizar los datos en más de 1 nivel, puedes traer más datos en una sola consulta.

Otro punto importante es la cantidad de datos. Mientras más datos tengas, será más claro el tema de la velocidad. En mi caso, tengo apps donde guardo mas de 300 documentos en el móvil y la velocidad es aceptable. Debemos considerar que los documentos no son tan simples, incluso la gran mayoría contiene BLOBs de 20K en promedio de tamaño con un tamaño total de 12 MB de la base de datos, lo que sería una locura en un entorno sqlite. Si el volumen de datos que vas a manejar es menor a mi ejemplo, entonces la diferencia de performance no la vas a sentir.

Guardo el tema de índices para el final porque es muy importante. Con Sqlite podemos crear índices donde y cuando nos dé la gana lo cual casi no tiene costo en procesamiento ni en almacenamiento. En el caso de PouchDB, los índices no se hacen por nada: Existe un índice por defecto que solamente contiene el campo _id de todos los documentos en la base de datos, y como no hay tablas pues estamos hablamos de absolutamente todos los registros. Suena a limitación, pero significa que recuperar un registro es super mega extra rápido y recuerden que un registro puede tener mucha información que en Sqlite podría tomar más de una consulta, además, si usamos el ID correcto nuestras consultas pueden beneficiarse de este super índice: la clave es crear los IDs manualmente incluyendo los criterios de búsqueda que necesitemos. Por ejemplo, si queremos guardar un documento para el Comic 1 de la editorial DC que salió en 01 de febrero del 2015, el ID que nos convendría sería com_dc_2015-02-01_01, de esta manera podremos hacer consultas muy rápidas con el método allDocs de PouchDB:

– Todos los comics: startkey: ‘com_’, endkey: ‘com_\uffff’;

– Todos los comics de la editorial DC: startkey: ‘com_dc_’, endkey:’com_dc_\uffff’

– Todos los comics de DC que salieron el 2015: startkey: ‘com_dc_2015_’, endkey: ‘com_dc_2015_\uffff’

La mala palabra en PouchDB es ‘Joins’ al estilo sql. No existe, así de simple, pero no desesperen pues existen muy buenas alternativas. Aquí van dos: Podemos crear vistas que son parecidas a las del mundo relacional pero que siguen el modelo Map/Reduce que es lo mejor en lo referido a escalabilidad, igual la recomendación es usarla solamente en condiciones extremas tal como se explica en este artículo. La segunda opción se llama Mango y es una forma de indexar toda la base de datos de la forma que nos de la gana y podrán encontrar información aquí.

Finalmente, y creo que es la razón definitiva por la cual se debería usar el modelo con PouchDB, es que tenemos que valorar que estamos usando una solución completa para el manejo de datos, no es solamente un modo de tener una base local, es una solución de sincronización. En un modelo Sqlite, la sincronización es un proceso que tiene que ocurrir de modo secuencial, es decir: ingresas datos, modificas datos, parar para sincronizar, lees datos, modificas datos. Con PouchDB, la sincronización es un proceso que se ejecuta en una tarea paralela, por lo que podremos ingresar, leer, modificar datos sin parar.

Si realmente necesitan velocidad, Sqlite no va a estar en el vecindario por mucho tiempo, así que mi recomendación está en mejorar el modelo.


Este post lo hago de urgencia pues acabo de experimentar todo lo que no se debe hacer en el tema de sincronización. Para esto les doy un resumen de mi entorno de desarrollo:

  • Servidor: Instancia en Cloudant.com
  • Cliente: PouchDB como base de datos con sincronización al iniciar el app
  • Framework: Ionic

En resumen, se inicia una sincronización entre mi base de datos local en PouchDB y la base remota en Cloudant. Y puede ser todo lindo en Producción, pero como estamos en Desarrollo, el proceso normal es parar e iniciar el app a cada rato. Ahora veamos las consecuencias en desarrollo:

  • Costo: Cloudant te cobra por transacciones y te dice que por transacciones “pesadas” te va a cobrar mas, y pone varias transacciones HTTP y las define como pesadas. Lo que no dice es que hay otras transacciones que se utilizan para la sincronización que también se califican como pesadas, principalmente OPTIONS que se manda a cada rato. Por lo tanto, cada sincronización es bastante intensiva en costo.
  • Tráfico: Para un modelo de desarrollo, no es bueno iniciar conexiones de datos muy seguido. Entonces, el modelo debe saber cuando es bueno iniciar la sincronización sin importar que sea en Desarrollo o Producción.
  • Batería: Como consecuencia de la reducción de tráfico, estaremos también bajando el consumo de energía en el móvil.

No malentiendan, CouchDB/PouchDB es muy bueno, y si existe la necesidad, podemos activar la sincronización “live”, pero como no todos tenemos servidores y ancho de banda de sobra, tenemos que pensar en servicios en demanda en la red, como Cloudant en mi caso. Tengan en cuenta que otros servicios como IrisCouch, también cobran por transacciones.

La salida que encontré depende de como se necesita que fluyan los datos:

  • El recolector de datos: si nuestra app necesita solamente capturar datos y no necesito bajar información desde el servidor. En este caso, lo mejor es implementar un contador que incrementaremos cada vez que se actualice o se inserte un dato nuevo. Lo bueno es que para un entorno NoSQL, insertar o actualizar es el mismo método, así que en ese método podremos insertar el incremento del contador. Luego, al momento antes de sincronizar, verificaremos si es que el contador es mayor a 0, de lo contrario no iniciaremos la sincronización. Sincronizar solamente cuando hayan datos locales.
  • El visualizador de datos: Aquí es totalmente al revés, el app jala información del servidor. En este caso, la primera alternativa es la de fijar un tiempo de sincronización relativamente alto, agregar un visor de la última fecha de sincronización y la opción para forzar la sincronización manualmente.
  • Finalmente, como siempre, en la vida real tendremos una combinación de necesidades, así que la recomendación final es la de utilizar un proceso de sincronizacion para descargar datos y otro para subir datos.

Hay una función que no he mencionado aún: Sincronización filtrada. Esto es super útil y lo voy a desarrollar en otro Post, Básicamente es la de descargar solamente la información que me interesa. Esto es tan importante que todos deben usarlo si es que tenemos que descargar datos.

En conclusión, es muy fácil sincronizar sin límites, pero en la vida real, no hay que pedir más de lo que debemos consumir.


Ya publiqué hace unas semanas mi primera aplicación multiplataforma y con el tiempo y las pruebas que vengo realizando, confirmo cada día que esta combinación es realmente de mucho potencial, por su facilidad y su potencia. Ahora creo que es tiempo de hacer un ejemplo “End to End” que muestre el proceso desde la teoría hasta la ejecución de un app. Así que nada de código para que te bajes, sigue paso a paso y tendrás al final de este post un app en javascript que sincroniza con una base remota y que de yapa puede funcionar en cualquier móvil. “Fasten your seatbelt” y nos vemos al final del Post.

Empecemos definiendo el problema: Tienes tu smartphone super ultra plus y quieres abrir algún App de los miles que tienes instalado y te das cuenta de que se te acabó el saldo y no estas ni siquiera cerca de un Starbucks, o estas en una zona donde la cobertura es recontra mala. Lo que normalmente sucederá es que el App te dirá que no hay red y que no se puede usar. Este escenario parece trivial pero para algunas aplicaciones, es un escenario que tiene que evitarse a toda costa. Llevando el tema más allá, creo que todas las aplicaciones deberían considerar que en algún momento la red se puede caer y deben poder seguir funcionando, al menos en un conjunto básico de funciones. Desde el punto de vista de programación estamos hablando de aplicaciones que soporten trabajar “Off-Line”.

Para hacer todo esto posible hay miles de recetas disponibles y la que presento ahora combina simpleza y potencia. Para ver como debemos comenzar veamos el gráfico:

gen01

Si vamos componente por componente:

  • Servidor: Cloudant es parte de la propuesta Bluemix de la plataforma de servicios cloud de IBM. Está basado en Apache CouchDB con algunas mejoras en el campo de autenticación, manejo de información geográfica y búsquedas. Digamos que es la implementación mas segura y confiable de CouchDB con la gran ventaja de que podemos acceder al nivel gratis del servicio muy fácilmente. De hecho, si generas un tráfico por un total de 50 USD o menos, no pagas y gracias al esquema que les voy a explicar, eso es un montón.
  • Cliente: Ionic Framework es una herramienta para el desarrollo de aplicaciones basadas en HTML 5 con javascript. Está basado en Apache Cordova y permite desarrollar para Android, iOS, Windows Phone 8, Windows 8, y otras mas aunque por ahora solamente Android y iOS son oficialmente soportados. Lo mas importante puede ser que Ionic está basado en AngularJS que es un framework para el desarrollo de aplicaciones JavaScript que facilita el desarrollo de aplicaciones basados en el modelo MVC.
  • Base de datos cliente: PouchDB es un proyecto que trata de implementar CouchDB en javascript. Debido a que el soporte de formatos de base de datos en los móviles es muy variado, PouchDB funciona además como una capa de abstracción de almacenamiento aunque su mayor fortaleza es en la implementación del protocolo de sincronización.

Hay algo que no he dicho aún y es que para los que estamos acostumbrados al mundo SQL significa días difíciles. Personalmente, me parece que el cambio ya se dió al enterarme que el estándar WebSQL, (SQLite para los amigos) ya no es el estándar en muchas de las plataformas móviles. Esto significa que tenemos que empezar a lidiar con NoSQL.

NoSQL significa que ya no hay esquemas o “tablas”, simplemente hay documentos que lo único seguro es que tienen un id único ( aunque por ahí ya vi un caso donde incluso esto no es cierto ). No se engañen, No Tablas no significa No Diseño, al contrario, el diseño tiene que ser mayor con la ventaja de que no hay esquemas que ir impactando. De nuestro diagrama, Cloudant y PouchDB son NoSQL y serán a partir de ahora la columna vertebral de nuestras aplicaciones móviles.

Bien, creo que las explicaciones ya están, ahora lo que queremos ver es una aplicación que nos muestre lo fácil que es entrar al mundo de móviles. Preparemos entonces el entorno.

Organizando las herramientas

Empecemos instalando las herramientas. Primero se debe instalar NodeJS. Es una serie de librerías Javascript que son necesarias para Ionic. En el link podrán encontrar la versión para los sistemas operativos más populares.

Con el NodeJS listo, vamos a instalar ahora Ionic. En una ventana de consola:

c:\>npm install -g ionic

No es requisito pero también deberían instalar Cordova:

c:\>npm install -g cordova

Y listo, Ionic ya está y lo pueden comprobar así:

c:\>ionic info

Ahora, hay que instalar PouchDB, para lo cual necesitamos instalar Bower que es una herramienta para instalar librerías JavaScript. Para eso utilizamos el siguiente comando:

c:\>npm install -g bower

Ahora lo que tenemos que hacer es crear un proyecto Ionic y agregarle las librerías de PouchDB. Para crear un proyecto Ionic, procedemos con el siguiente comando:

c:\>ionic start superdatos

Mediante este comando estamos creando el proyecto llamado “superdatos”. Se ha creado una carpeta con todos los archivos necesarios. Una vez que el comando haya terminado debemos hacer la configuración de SASS que es algo super potente que explicaremos después, por ahora simplemente ejecuten el comando siguiente:

c:\>cd superdatos
c:\superdatos\> ionic setup sass

Para los que entienden CSS, SASS es un CSS con parámetros. Yo todavía no lo entiendo bien, pero lo poco que he hecho es suficiente para darme cuenta de su importancia, así que ahi está. Ahora instalemos PouchDB:

c:\superdatos\>bower install pouchdb

Y listo. Si quieren ver lo que han hecho hasta ahora entonces ejecuten el siguiente comando:

c:\superdatos>ionic serve

Ahora abran un navegador, ingresen el url http://localhost:8100 y listo:

gen07

Así es, sin darse cuenta han hecho una aplicación con una barra de título. Casí nada, pero al menos ya hemos comenzado

Organización de la aplicación

Ahora revisemos todo lo se que ha hecho. Veamos los archivos:

En la carpeta raíz no hay mucho que ver:

gen05

Nos enfocaremos inicialmente en la carpeta www

gen06

  • index.html: es el punto de entrada de nuestra aplicación y donde registramos todas las librerías que queremos usar
  • Carpeta js: es donde colocamos todos los archivos javascript. El principal archivo javascript es app.js que es donde configuramos nuestra aplicación.
  • Carpeta lib: Si ingresan es donde están grabadas todas las librerías que tenemos disponibles para la aplicación. Ionic y PouchDB estarán ahí, además de otras propias de Angular
  • Carpeta img: obvio, las imágenes van aquí.
  • Carpeta css: y aquí van los archivos CSS

Y listo. Ahora nos toca entender por donde comenzar, para lo cual debemos entender el concepto de MVC o Model – View – Controller.

MVC-web-application-development

Según esto, una aplicación se divide en estos tres elementos: modelo, vista y controlador, donde vista es toda la parte gráfica de la aplicación y como el usuario interactúa con ella. El controlador es quien maneja la lógica detrás de las pantallas, cuál es la lógica que se lanza al hacer click en un botón. El modelo es como la lógica del negocio, que también controla lo que grabamos en una base de datos, si al hacer click en un botón que dice grabar, el controlador le tendrá que decir al modelo que tiene grabar una entrada de producto, seguidamente, la vista se refrescará para mostrar todas las entradas de producto. Exacto, el producto es el modelo.

Ya dijimos que Ionic se basa en AngularJS donde es muy fácil identificar al modelo, la vista y el controlador, de hecho sería así:

  • Vista : carpeta templates, archivos HTML o simplemente index.html
  • Controlador: carpeta js, archivos js. Entidad controller en AngularJS
  • Modelo: carpeta js, archivos js. Entidad factory en AngularJS

Es mas simple de lo que parece. Y para eso revisemos nuestra simple aplicación tal cual está ahora. Primero veamos el archivo index.html que tiene definida la vista:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title></title>
<!-- compiled css output -->
<link href="css/ionic.app.css" rel="stylesheet">
<!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script>
<!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script>
<!-- your app's js -->
<script src="js/app.js"></script>
</head>
<body ng-app="starter">
<ion-pane>
<ion-header-bar class="bar-stable">


<h1 class="title">Ionic Blank Starter</h1>


</ion-header-bar>
<ion-content>
</ion-content>
</ion-pane>
</body>
</html>

Las líneas que nos interesan por ahora son la 14 y la 16. Primero en la 14 veremos

<script src="js/app.js"></script>

Esta línea le dice a la aplicación que la lógica que necesita está en el archivo app.js que está escrito en javascript como podrán sospechar. Cualquier otra librería tiene que ser declarada aquí para poder ser utilizada. Recuerden esto pues tenemos que regresar aquí mismo para ver que hacemos con PouchDB.

<body ng-app="starter">

Y en la línea 16 le decimos que la aplicación que estamos haciendo es ng-app osea Angular con nombre “starter”.

Ahora revisemos el famoso app.js:

// Ionic Starter App
// angular.module is a global place for creating, registering and retrieving Angular modules
// 'starter' is the name of this angular module example (also set in a <body> attribute in index.html)
// the 2nd parameter is an array of 'requires'
angular.module('starter', ['ionic'])

.run(function($ionicPlatform) {
  $ionicPlatform.ready(function() {
    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
    // for form inputs)
    if(window.cordova && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
    }
    if(window.StatusBar) {
      StatusBar.styleDefault();
    }
  });
})

En la línea 5 está definido el módulo ‘starter’ que es el que encontramos en el archivo index.html. Cuando index.html quiera saber que hacer, vendrá a buscar la lógica a este archivo app.js.

Luego en la linea 7 vemos algo importante. Para los que ya tienen experiencia con aplicaciones en Cordova, entenderán la importancia del evento CordovaReady. Para ponerlo simple, Ionic es Cordova en esteroides, así que también hay que esperar por el evento CordovaReady, pero como Ionic habla el dialecto AngularJS entonces tenemos que usar el código tal cual está aquí.

Integrando PouchDB

Pues bien, ya estamos listos para integrar nuestra base de datos así que debemos preguntarnos ¿qué queremos hacer? Pongamos las cosas en el nivel mas simple posible. Digamos que queremos hacer un lector de noticias donde las noticias mas nuevas se muestren al comienzo. Nada complicado. Con esto en la mente, comencemos agregando activando PouchDB en nuestro proyecto, de la siguiente manera. Nuestro archivo index.html debería ser así ahora:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
    <title></title>
    <!-- compiled css output -->
    <link href="css/ionic.app.css" rel="stylesheet">
    <!-- ionic/angularjs js -->
    <script src="lib/ionic/js/ionic.bundle.js"></script>
    <!-- cordova script (this will be a 404 during development) -->
    <script src="cordova.js"></script>
    <!-- your app's js -->
    <script src="js/app.js"></script>
    <script src="lib/pouchdb/dist/pouchdb.js"></script>
  </head>
  <body ng-app="starter">
    <ion-pane>
      <ion-header-bar class="bar-stable">


<h1 class="title">Ionic Blank Starter</h1>


      </ion-header-bar>
      <ion-content>
      </ion-content>
    </ion-pane>
  </body>
</html>

Hemos agregado una línea indicando donde está el archivo con la librería PouchDB. Si se fijan bien, dentro de nuestra carpeta lib está la carpeta con toda la info requerida por PouchDB, aquí simplemente hemos apuntado al archivo de distribución.

NOTA: es una buena idea que tengan una consola abierta solamente para que el comando Ionic serve esté corriendo todo el tiempo ya que Ionic permite que todo lo que hacemos se compile en tiempo real.

En Google Chrome veremos que nuestra app no ha cambiado mucho. Nos toca entonces, crear la base de datos. Para eso, agregamos la siguiente línea en nuestro app.js:

// Ionic Starter App

// angular.module is a global place for creating, registering and retrieving Angular modules
// 'starter' is the name of this angular module example (also set in a <body> attribute in index.html)
// the 2nd parameter is an array of 'requires'
angular.module('starter', ['ionic'])

.run(function($ionicPlatform) {
  $ionicPlatform.ready(function() {
    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
    // for form inputs)
    if(window.cordova && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
    }
    if(window.StatusBar) {
      StatusBar.styleDefault();
    }
    var db = new PouchDB('news');
  });
})

En la línea 18, creamos la variable que tendrá nuestra base de datos local y le ponemos de nombre ‘news’. Al grabar los cambios, notarán que el navegador se refresca solo y volvemos a ver nuestra app. Parece que todo sigue igual pero no. Para desengañarnos, tenemos que abrir las herramientas (Ctrl + Mayus + I) y vamos a la sección Resources y veremos:

gen08

En recursos, veremos que se ha agregado una base de datos con el nombre _pouch_news y no es coincidencia. Es nuestra base de datos ‘news’ creada por PouchDB en el formato IndexedDB. Todo bien hasta ahora. Ahora veamos como nos conectamos a IBM Bluemix para acceder a Cloudant.

Jugando con Cloudant

En el mundo CouchDB, encontré muchas alternativas “cuasi” gratuitas para comenzar a trabajar pero es lamentable como la falta de visión a largo plazo hace que la confiabilidad no sea algo característico. Todo esto se terminó con Cloudant, ya que IBM es quien está detrás y eso es ya bastante. ¿Porqué no instalar mi propia instancia de CouchDB en mi PC? Simple, Cloudant es gratis hasta 50 USD de tráfico, es super rápido, confiable y otros adjetivos todos muy buenos. Además, estamos en la onda de utilizar herramientas Cloud así que va a tono.

Como siempre, primero hay que crear una cuenta en https://cloudant.com/sign-up/ y ya podrán acceder a esta pantalla:

gen09

Para los fans de CouchDB, esta es una versión algo mejorada de Fauxton con las mejoras que les comenté al inicio. No perdamos mas tiempo y creemos nuestra primera base de datos. Un click en Add new Database, ponemos el nombre ‘news’ por facilidad y listo, veremos esta pantalla ahora:

gen10

Ahora es el momento de reflexionar sobre la estructura de datos que necesitamos. Una noticia tiene un titular, un resumen y el cuerpo, además, tiene una fecha y un autor. Para comenzar, creo que es suficiente, así que agregaremos nuestro primer documento. Hacemos click en el signo mas junto a All Documents  y seleccionamos nuevo documento. Y luego empecemos a crear nuestro documento en formato JSON:

gen12

Simple, ¿Queremos mas campos? no hay problema, le agregas nomas al JSON y listo. ¿Y los índices, llaves primarias, foráneas y otros? Pues no hay, al menos no aquí. Lo único que hay que respetar aquí es que el campo “_id” es obligatorio y debe ser único. ¿Y la clave no es autoincrement? no necesariamente, podemos dejar que se autogenere pero nos perdemos de mucho si hacemos eso. Si se dan cuenta la _id que estoy usando es “news_20150507_vpease_001” que sospechosamente parece ser hecha a propósito y la verdad es que si. Es altamente recomendable utilizar una convención para la creación de claves. En este caso:

  • “news” : indica el tipo de documento. Recuerden que no hay esquemas así que mejor si identificamos así a nuestras entidades.
  • “20150507” : así es, se refiere a la fecha, pues ya dijimos que queremos mostrar las últimas noticias primero.
  • “vpease” : correcto otra vez, es el autor de la noticia.
  • “001” : una pequeña cadena al final para asegurar que el id sea único.

¿Porque todo este lio con la clave? Si bien es cierto que tenemos un campo “tipo” que nos permite diferenciar las entidades entre todos los documentos de la base, CouchDB tiene un método super rápido llamado AllDocs que sólo puede ser ordenado por el campo “_id”. Podemos crear nuevos indices pero ocupan espacio adicional, así que para ahorrarnos algo de espacio y ganar algo de velocidad, NO DEBEMOS AUTOGENERAR LA CLAVE, SIEMPRE DEBEMOS COMPONER LA CLAVE CON CAMPOS DE ORDENACION.

Y finalmente, grabamos y listo.

gen13

Notarán que se ha agregado un campo “_rev”, esto es un mecanismo interno de CouchDB para controlar los cambios y hacer lo que mejor hace: sincronizar.

Podríamos agregar mas noticias, pero por ahora es suficiente. Finalmente tenemos que crear algún tipo de identidad o usuario para poder acceder a los datos. Esta es una de las mejoras de Cloudant, todas las llamadas deben ser autenticadas y el HTTPS viene de gratis. En Cloudant, volvamos a la lista de base de datos:

gen14

Entremos a la sección de permisos ahora:

gen15

Hay dos maneras de dar acceso:

  • Le damos permiso a todos a entrar a los datos
  • Crear un API key/clave

Hay una tercera opción que es la de usar nuestras credenciales administrativas para dar acceso, lo cual es a todas luces incorrecto, como también es incorrecta la opción 1, así que API Keys es la voz. También se puede compartir la base a otro usuario de Cloudant pero eso no es masivo, así que nos quedamos con las API keys. En simple, un API key es un usuario temporal que solamente sirve para acceder a los datos y le podemos asignar permisos. Para mantener todo simple, vamos a darle a esta API key los privilegios de Replicator y Reader.

gen16

Y listo, apunten bien esta combinación de Key y password porque ya no puede volver a generarse. Podrán generar una nueva Key pero ya no podrán recuperar la clave. Fijense que ya le asignamos los privilegios de Reader y Replicator. Ahora, toca finalizar nuestro app cliente.

NOTA CRITICA: Antes de salir hay que activar CORS, así que denle click en Account – CORS y activen All Domains. ¿Qué es CORS? es una protección de los navegadores para no ejecutar código remoto. Para nuestro caso, CORS debe estar siempre activado. La pantalla debe lucir así:

gen18

Agregando la sincronización 

Para esto, debemos entender un tema previamente. La sincronización tiene muchas variantes pero por ahora sólo nos va a importar tres cosas: Dirección, Frecuencia y Eventos.

La dirección se refiere al sentido que tendrán los datos, del Servidor al móvil o del Móvil al Servidor. Dado que solamente vamos a leer, consideraremos la sincronía de Servidor al Móvil.

La frecuencia se refiere a que tan seguido queremos actualizar los cambios. Convenientemente, PouchDB tiene una opción “live” que tratará  de actualizar los datos lo más rápido posible, suena bien pero es mas costosa del lado de Cloudant, así que debemos usarla con mucho cuidado. Igual siempre podemos sincronizar manualmente cuando lo creamos conveniente.

Finalmente, los eventos nos indican como va el proceso. En resumen:

  • change (info) – Generado cada vez que se detecta un nuevo documento escrito.
  • complete (info) – Se genera cuando una sincronización manual termina o cuando se interrumpe una sincronización live.
  • paused (err) – cuando una sincronización live ha terminado y espera por nuevos cambios o cuando se ha interrumpido por algún err.
  • active – cuando una sincronización comienza o cuando se recupera de algún error.
  • denied (err) – cuando las credenciales son rechazadas por el servidor.
  • error (err) – cuando se genera un error irrecuperable como cuando se corta la red.
  • uptodate (deprecated) – Este evento ya esta de salida así que no lo usen

Ahora, como por arte de magia, agregaremos las líneas para activar la sincronización. La magía será en el archivo app.js.

// Ionic Starter App

// angular.module is a global place for creating, registering and retrieving Angular modules
// 'starter' is the name of this angular module example (also set in a <body> attribute in index.html)
// the 2nd parameter is an array of 'requires'
angular.module('starter', ['ionic'])

.run(function($ionicPlatform) {
  $ionicPlatform.ready(function() {
    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
    // for form inputs)
    if(window.cordova && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
    }
    if(window.StatusBar) {
      StatusBar.styleDefault();
    }
	var key = 'bentareadyessharyinessee';
	var pass = 'OnEixgKgpt8LyEtl0S5DkAon';
	var remote = 'https://'+key+':'+pass+'@supermio.cloudant.com/news';
	var db = new PouchDB('news');
	db.replicate.from(remote,{live:true,retry:true});	
  });
})

Veamos, simplemente hemos agregado unas variables con el API Key y la clave de acceso y el url de la base de datos en Cloudant. Verán que estoy agregando las credenciales al url, sería algo asi: https://user:pass@servidor/basededatos.

En la línea 22 está la línea que hace la verdadera llamada a la sincronización donde hemos indicado un par de parámetros: live y retry que como su nombre lo indica, activarán la replicación “live” y en el caso de algún error, la replicación se intentará nuevamente.

Veamos como se ve la app hasta ahora en Chrome, primero en la consola:

gen17

Si vemos líneas con XHR significa que ya estamos sincronizando. Y para que me crean vamos al tab Resources:

gen20

Y ahí vemos nuestro documento!! Es cierto!

Guardemos ánimo para hacer lo que de veras hace falta que es presentar los datos.

Finalmente, Presentando los datos

Antes de presentar los datos debemos tomar en cuenta los eventos pues recuerden que la base de datos en el móvil comienza vacía y luego irán llegando las noticias debemos poder refrescar la vista. Los eventos ya los vimos así que recordemos que estamos haciendo una replicación “Live” y cuando acabe se generará un evento “paused”, también podríamos usar “change” pero este evento sólo funcionaría para los nuevos cambios. Si por alguna razón, la base remota y la local están iguales, el evento “change” no se generaría, mientras que “paused” si. Primero veamos como va la cosa con “paused”. Para eso agregamos las siguientes líneas a app.js justo después del comando de replicación:

.on('paused',function(info){
		db.allDocs({startkey:'news_\uffff',endkey:'news_',descending: true,include_docs:true})
		.then(function(result){
			$rootScope.$broadcast('refrescar',result.rows);			
		});		
	})

Veamos línea por línea:

  • Línea 23: agregamos un manejador para el evento “paused”. Fijense que el comando db.replicate no tiene el “;” al final, por lo que “.on” es una propiedad del comando “db.replicate”. Luego de la “,” tenemos la función que se ejecutará cuando ocurra el evento.
  • Línea 24: aquí viene el primer choque para los que venimos del mundo SQL. El comando “db.allDocs” es el comando más potente de PouchDB y de hecho de CouchDB también y permite recuperar documentos mediante el índice principal que si se habrán dado cuenta, incluye a todos los documentos en la base de datos. Inicialmente, allDocs recupera todos los documentos, pero toma parámetros para limitar un poco eso. Por ejemplo, aquí le hemos pasado los dos primeros “startkey” y “endkey” que se refieren a algo así como un like y como notarán la cadena que pasamos es “news_” y agregándole el caracter “\uffff” es como ponerle el asterisco, por lo que los dos primeros parámetros significan que debemos traer todos los documentos cuya clave comience con “news_” que gracias a la estructura de claves que fijamos es equivalente a decir que traiga todas las noticias en la base de datos. Seguidamente verán que hemos fijado “descending” a “true” y eso es por la fecha, si recuerdan la estructura de nuestra clave que era “news_fecha_autor_XXX” así que decirle que sea descendente significa que las noticias más recientes estarán al principio, lindo truco. Finalmente, con “include_docs” le decimos que nos traiga todos los datos en el documento, de lo contrario solamente tendremos la clave.
  • Línea 25: Aquí vemos una de las mayores características de aplicaciones NodeJS, Promesas. Dado que db.allDocs se ejecuta de forma asíncrona, no sabemos en que momento ejecutará la siguiente línea, así que con “.then” le decimos que al terminar, ejecute la función incluida.
  • Línea 26: La función que pasamos como promesa ejecuta una sola línea y esta es: “$rootScope.$broadcast” que en simple significa que se enviará un mensaje a todos los contextos indicando que un evento se ha realizado. El evento se llamará “refrescar” y tendrá como parámetros todos los “rows” incluidos en el objeto result, o más simple, todos los documentos recuperados desde la base de datos.

Y listo, hasta ahora hemos trabajado en lo que puede decirse que es el “Controller” de toda la aplicación y como no está asociada a ninguna vista, no podremos crear ninguna lógica que presente los datos. Entonces ahora tenemos que ligar la vista que tenemos ( osea “index.html”) con algún controller.

Primero en la vista “index.html” declaramos el controlador asi:

<body ng-app="starter">
    <ion-pane ng-controller="newsController">
      <ion-header-bar class="bar-stable">


<h1 class="title">Ionic Blank Starter</h1>


      </ion-header-bar>
      <ion-content>
      </ion-content>
    </ion-pane>
  </body>

En la línea 18 verán que hemos agregado la directiva “ng-controller” donde le indicamos a la vista que el controlador es “newsController”. Ahora definamos el controlador en el archivo “app.js”. Pongamos todo el código en el archivo pues los “;” y “.” se vuelven muy importantes:

// Ionic Starter App

// angular.module is a global place for creating, registering and retrieving Angular modules
// 'starter' is the name of this angular module example (also set in a <body> attribute in index.html)
// the 2nd parameter is an array of 'requires'
angular.module('starter', ['ionic'])

.run(function($ionicPlatform,$rootScope) {
  $ionicPlatform.ready(function() {
    // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
    // for form inputs)
    if(window.cordova && window.cordova.plugins.Keyboard) {
      cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
    }
    if(window.StatusBar) {
      StatusBar.styleDefault();
    }
	var key = 'bentareadyessharyinessee';
	var pass = 'OnEixgKgpt8LyEtl0S5DkAon';
	var remote = 'https://'+key+':'+pass+'@supermio.cloudant.com/news';
	var db = new PouchDB('news');
	db.replicate.from(remote,{live:true,retry:true})
	.on('paused',function(info){
		db.allDocs({startkey:'news_\uffff',endkey:'news_',descending: true,include_docs:true})
		.then(function(result){
			$rootScope.$broadcast('refrescar',result.rows);			
		});		
	})
  });
})
.controller('newsController',function($scope){
        $scope.notas='';
	$scope.$on('refrescar',function(event,news){
		$scope.$apply(function(){
                      $scope.notas = news;
                })
	})
})

Primero veamos todo el archivo en panorama y notaremos que el módulo ‘starter’ tiene propiedades “.run” y “.controller” que hemos definido. Esto se puede notar porque no hay “;” que los separe justo en la línea anterior. Ahora revisemos las líneas nuevas:

  • Línea 31: definimos el controlador ‘newsController’ y la funcion asociada que utiliza la entidad $scope. $scope es una entidad AngujarJS que identifica al ámbito definido en la vista que para nuestro caso es ‘index.html’ pero solamente en la sección donde está “ng-controller” definida. Para ponerla simple, nos permite declarar variables y funciones que serán visibles dentro de la vista.
  • Línea 32: declaramos una variable en el entorno llamada “notas”
  • Línea 33: Si la replicación envía eventos “paused”, pues es necesario que los recibamos en alguna parte así que aquí definimos que para cuando se reciba el evento ‘refrescar’, ejecutemos la función anexa la que toma dos parámetros: el evento y el objeto asociado donde están nuestros documentos.
  • Línea 34: En angular, si cambiamos el modelo tenemos que “aplicar” el cambio en el $scope
  • Línea 35: Finalmente, asignamos el array de documentos a una variable de entorno llamada “notas”

En la línea 34, sin notarlo, también estaremos usando una de las grandes potencias de AngularJS: “two-way data binding” que es el refresco automático de la vista cuando cambien las variables del entorno. Créanme que es mas fácil verlo que explicarlo.

Veamos, ya el controlador tiene los datos, así que vayamos a la vista a mostrar los datos. Para esto agregamos el siguiente código a “index.html”:

<ion-content>


 Hay {{notas.length}} noticias



<div class="list" ng-repeat="noticia in notas">


<div class="card">


<div class="item item-divider">{{noticia.doc.fecha}}</br>{{noticia.doc.titular}}</div>




<div class="item item-text-wrap"><b>{{noticia.doc.resumen}}</b></div>


			


<div class="item item-divider">Autor: {{noticia.doc.autor}}</div>


		</div>


	  </div>


      </ion-content>

Otra vez el línea por línea:

  • Línea 23: mostramos el tamaño de la variable notas y como es un Array de documentos acepta la propiedad length
  • Línea 24: Hacemos una lista para que se vean las noticias algo ordenadas mediante la clase CSS “list” y dejamos abierto el tag para agregarle otra directiva
  • Línea 25: al <div> de la lista le agregamos una directiva Angular para iterar en los elementos del array “notas” y a cada elemento lo conoceremos como “noticia”
  • Línea 26 a la 30: Cada elemento del array trae mucha información desde Cloudant, pero nos interesa uno en particular que es la propiedad “doc” que tiene todos los campos que hemos definido, así que lo incluimos. Además, incluimos el nombre de cada campo del documento noticia.

Graben y listo. Ya tienen su primera aplicación en Ionic con integración de base de datos local PouchDB y remota en Cloudant!!. Para ver más allá de lo evidente, pueden hacer la prueba agregando otro documento en Cloudant para que vean la potencia de la sincronización con PouchDB. Ya saben como agregar documentos a Cloudant así que tomen su reloj y tomen el tiempo que toma mostrarse la nueva noticia. Recuerden usar el formato para el campo “_id”: “news_fecha_autor_XXX”. En mi caso, el refresco fue casi instantáneo.

Y con esto podemos dar por terminado este largo Post con la satisfacción de tener una app completamente funcional. Para los que recién comienzan, los tags que vienen del mundo Ionic pueden confundir (<ion-pane>,<ion-content>, etc) pero no hay nada como leerse la documentación en la página de Ionic Framework pues está con ejemplos y todo. Para el caso de AngularJS, el peligro es que en javascript hay muchas maneras de hacer lo mismo y ya les adelanto que lo que les he explicado aquí es una forma muy simple de hacer las cosas. Una aplicación real hace la separación de la lógica en varios módulos, mientras que nosotros hemos usado uno solo para todo. Igual con las vistas, se suele tener muchas y no una sola. En lo que se refiere a Cloudant, hemos utilizado la instancia remota solamente cuando se sincroniza y luego la consulta a los datos ya es hecha localmente al PouchDB por lo que nuestro consumo será muy bajo. Además, si bien la sincronización “live” es muy potente, en una app real verán que a veces es mejor hacer una sincronización manual, sobre todo si sabemos que los cambios no se generan a cada rato, lo que nos permitirá ahorrar aún mas en Cloudant. Aún queda mas que aprovechar de Cloudant como las vistas (el famoso map/reduce de CouchDB), como manejar el almacenamiento, Sincronización filtrada  y algunas otras optimizaciones, todo con la seguridad de que sus datos están seguros y disponibles.

A ver si preparo el siguiente Post sobre vistas en Cloudant para poder combinar mas tipos de dato en el App y como generar sus apps para Android o iOS gracias a Ionic. Por ahora, jueguen con el app y modifíquenlo para que se vayan acostumbrando.

Mas información:

AngularJS: http://angularjs.org

Ionic Framework: http://ionicframework.com

PouchDB: http://pouchdb.com

IBM Cloudant: http://cloudant.com


Hay algo que se obtiene apenas tomas el iPhone 5s en la mano y es la satisfacción de tener un equipo elegante. Si además puedes desbloquear la pantalla con tan solo poner tu huella, entonces se vuelve una adicción. Realmente Apple aún tiene el toque para hacer equipos que dan gusto utilizar.

Usabilidad: Regresemos al trabajo y me sorprende encontrar que es muy fácil y práctico utilizar el iPhone y su velocidad es muy buena, hasta que tenemos que empezar a ingresar algún texto. El teclado no es malo pero tampoco tan bueno como uno físico.

Las notificaciones es un tema donde si reniego. Mientras que en Android yo puedo ocultar una notificación simplemente arrastrándola, en iPhone sólo puedo borrarlas por grupos, además, si quiero ver realmente que notificaciones tengo pendientes, tengo que abrir el centro de notificaciones pasando el dedo de arriba a abajo, de lo contrario no me entero que hay cosas pendientes. Mal.

Nunca me ha pasado que alguna aplicación se pone lenta o se cuelga, incluso cerrar apps es muy fácil mediante una función copiada de WebOS.

En la parte de correo, la aplicación cliente no es la maravilla pero hace el trabajo, aunque se hace problemas con archivos adjuntos muy grandes. Principalmente, debo resaltar que el soporte para los proveedores de correo mas populares es tan bueno que puedo sin ningún problema sincronizar contactos, calendarios de forma muy fácil, algo que extrañé en Android. El único problema con esto es que iOS se cree tan inteligente que a veces, combina varios contactos en uno solo cuando por algún criterio oscuro cree que se trata de la misma persona. Sugerencia, arregla bien tus listas de contactos antes de sincronizar.

Un punto importante es el Office. Para iOS ustedes sólo podrán modificar archivos si tienen una cuenta de Office365 así que les toca pagar, algo que en otras plataformas es gratis, asi que mientras no lo hagan, sólo podrán visualizar. Así como Office hay otras apps que sólo tienen versión de pago, como por ejemplo Whatsapp así que vayan haciendo algo de caja para actualizar sus apps.

Batería: me sorprendió además que la batería funciona bien, siempre y cuando me dedique a trabajar. Esto no es nada nuevo. Básicamente, si te dedicas a usar cualquier otra app que no sea correo, teléfono o contactos, tu batería va a sufrir y esto también se cumple aquí con iOS. El otro punto es el manejo de las notificaciones Push.

Ya les dije que el cliente de correo es bueno y las opciones de sincronización también lo son, por lo tanto, eso nos permite dejar de usar aplicaciones propias de proveedores como Yahoo o Outlook.com que además de consumirnos recursos, nos consume datos y batería mediante las notificaciones Push. Aquí la regla es que si tenemos una aplicación que tiene la capacidad de alertarnos de contenido nuevo mediante notificaciones, No permitamos que utilice notificaciones. Cada notificación supone consumo de datos y por lo tanto de batería. O sea, fuera Instagram, Facebook, Vine y todas esas, téngalas instaladas y refresquen manualmente para ahorrar algo de batería.

Al final, si se pudieron controlar y dejaron de ver videos de Youtube en su iPhone, puede que lleguen al final de día con batería, o sea igual que el resto de opciones hasta el momento.

Una verdadera molestia es que este teléfono no tenga un puerto micro USB, por lo que tendrán que llevar su cable a todas partes por si es que se quedan sin batería.

 

En conclusión, iPhone 5s es un buen teléfono si es que soy un consumidor de datos, es decir, me la paso leyendo todo lo que me mandan y si contesto algún email, será sólo un par de líneas. Para trabajar a full quizá me quede algo corto tanto en funciones como en batería, pero estaré en la onda si me toca un almuerzo de negocios o una reunión en un bar.


Por un tema técnico, tuve que dejar la revisión del iPhone 5s y hacer la de BB10 Z10 y puedo decir que estoy gratamente impresionado.

Primero, z10 no es un mal equipo principalmente porque su Hub es realmente bueno para enfocarse en los correos electrónicos y mensajes de todo tipo, es como una bandeja de notificaciones, realmente muy práctico. Desde su salida se le criticó por la falta de aplicaciones  y realmente es un problema que sigue en la actualidad, siempre y cuando busquemos aplicaciones nativas BB10 porque una capacidad poco comunicada y explotada permite a estos equipos ejecutar aplicaciones Android, algo que pude comprobar y con un resultado impresionante en algunos casos. Es una pena que Blackberry haya cometido el error de no comunicar propiamente esta funcionalidad, así como otras como la gestión de conexiones y notificaciones.

Usabilidad: Z10 no tiene botones físicos, solamente el de Power y nada más, lo que significa que todo se hace a través de gestos en la pantalla, lo cual esta bien por un rato pero luego se vuelve muy incómodo. Si instalamos muchas aplicaciones, cambiar de aplicación activa se vuelve un ejercicio de gimnasio porque hay que pasar páginas y páginas.

Como era de esperarse leer correos y notificaciones es de lo mejor del equipo, ya que se presentan incluso con la pantalla bloqueada y se puede hacer un vistazo del contenido sin tener que desbloquear el equipo, o ir directamente al mensaje si es que quiero. Sweet.

El teclado no es una maravilla pero es bastante grande y cómodo sin llegar a ser excelente, los que tienen dedos gordos podrán utilizarlo sin problemas.

La configuración de los correos es simple y cubre con una sola aplicación todos los servicios de mensajería, incluyendo algunas redes sociales (Específicamente, Linkedin, Facebook y Twitter) otras redes simplemente no existen como clientes nativos porque recordemos que este es un teléfono de trabajo, pero como ya dije esto se puede solucionar con un app de Android. Para cerrar, el correo se notifica casi tan bien como con la versión clásica de BB y la visualización de los adjuntos es bastante buena. Otro aspecto fundamental es que la sincronización de contactos ya viene para todos los proveedores de mensajería por lo que no tendremos que preocuparnos por dejar algún contacto sin ser reconocido.

BBM está presente y es muy bueno, pero lamentablemente todos mis contactos ya se fueron a otras plataformas así que ya no encuentro a nadie por ahi.

En conclusión, si tuviera un par de botones  mas, el Z10 sería ideal para trabajar, ahora si tuviera un teclado QWERTY sería perfecto. Una lástima que el Q10 tenga un teclado pero con una pantalla reducida, algo que baso el esquema de navegación que tiene BB10 es de un gran impacto.

Ahora, explicaré rápidamente la compatibilidad con Android: Blackberrry hizo bien en implementar la ejecución de apps de Android en BB10, pero se olvidó de algo fundamental, hacer que instalar esas apps en el equipo sea sencillo. Entonces, si es que ustedes quieren ejecutar, por ejemplo, Instagram, tendrán que conseguirse el apk (lo cual es bastante complicado) y pasarlo al equipo (peor) o instalar un Appstore de terceros tal como Amazon store, 1- Mobile o culaquier otro por ahi. Personalmente, recomiendo 1-Mobile (1mobile.com) porque tiene un catálogo mas amplio incluyendo algunas apps propias de Google, inexistentes en el catalogo de Amazon. Lo que sigue es muy simple, elegir la app, descargarla, instalarla y listo.  Como ya estarán sospechando, no todas las apps de Android funcionarán bien, aquí sólo es cuestión de probar, por lo menos he podido comprobar que Google Maps y Instagram funcionan Ok.

Batería: Como también era de suponer, BB hizo un trabajo interesante con el uso de energía a pesar de sus capacidades de multitarea. A pesar de esto, llegar al final del día con carga dependerá de la cantidad de apps que tengan y el tipo de las mismas. Como era de esperar, si instalan apps de Android consumirán mas que si instalan apps nativas. La gran mayoría de apps de BB10 son html5 por lo que su consumo es limitado. Como regla general si tienen un app que use muchas figuritas, sonido y acceso a la red entonces estamos hablando de un alto consumidor de batería.

Igual que con Android, hay que salir a la calle con un cargador. Antes, hay que decidir bien que apps necesitamos verdaderamente.

Consumo de datos: Siendo el correo electrónico la app principal, es la que mejor ha implementado el uso de recursos, así que mientras usemos el BB Hub, estaremos seguros que usaremos la menor cantidad de datos de nuestro plan. Hay que anotar que con BB10 ya no tendremos los servicios de Blackberry.net, al menos no de la forma como estaba en los equipos BB7. Ahora, utilizaremos cualquier plan de datos regular.

Una diferencia fundamental es el manejo de notificaciones. El BB Hub es el app que maneja todo y seremos nosotros quienes decidiremos si aceptar la notificación o no. Entonces, si en Android, una notificación puede iniciar una sincronización sin avisarnos, en BB10 la sincronización se dará si es que aceptamos la notificación. Simple y sobre todo seguro para el usuario.

En conclusión, BB10 es una herramienta especializada para gente que quiere trabajar en la calle, tal vez no sea tan bueno como los equipos con BB7 para el ingreso de texto, pero cumple con su cometido siempre y cuando el correo electrónico sea nuestro único interés porque tendremos que “perdonar” muchas falencias en otros aspectos que son naturales para otras plataformas.  La compatibilidad con Android ayuda un poco pero no ha sido comunicada propiamente.

Para el trabajo podría utilizar un BB10, pero tendría que salir a la calle con otro smartphone para contestar llamadas y demás apps para asegurar que la batería me dure todo el día. Podría contestar correos en el Z10, pero no muy largos. Además, si es que el volumen de correos y cuentas es bastante alto, ésta sería mi única alternativa para poder salir a trabajar a la calle sin problemas.





%d bloggers like this: