Posts Tagged ‘Handhelds’


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 este punto debemos hacer un repaso y considerar algunos puntos que potencian la aplicación.

Primero, comencemos que el código está disponible en Github en la siguiente dirección:

https://github.com/victorpease/SuperDatos

Para que comiencen a verlo funcionar tendrán que seguir los siguientes pasos:

  1. Crear una cuenta en cloudant.com
  2. Crear una base de datos
  3. Crear una key en la sección de permisos y asignarle permisos de Writer
  4. Actualizar la key y la clave en el archivo services.js
  5. Actualizar la url de la base de datos también en el archivo services.js

Con esto ya podrán tener funcionando su propio lector de noticias y verán que cada vez que alguien lea una noticia, un registro será añadido a la base de datos y podrán con esa información hacer reportes sobre las noticias más populares, que luego podremos incluir como un reporte en la misma aplicación.

Bien, ahora los consejos:

  • Hasta ahora hemos considerado la sincronización Live, pero tengan en cuenta que genera mucho tráfico y por lo tanto costo, además, el cobro es por transacción, así que no importa mucho el tamaño de la base de datos. En Cloudant dan gratis un saldo de 50 dólares y eso es bastante, pero para una aplicación seria esto se puede salir de control. Lo mejor es que desactiven Live y que sincronicen manualmente cuando ocurra algún evento relevante. Para que vean la diferencia, Live sincroniza datos cada 25 segundos, así que si cambian que la sincronización sea cada 1 minuto, ya estarían bajando su consumo a la casi la mitad.
  • Si pensamos en este modelo “Offline-First”, es porque no confiamos en el acceso a la red. El mayor impacto es la primera sincronización. Piensen en usar el plugin PouchDB Load, que permite que hagan una carga inicial de datos en el móvil en lugar de esperar los datos de la red. El archivo de texto tiene que ser generado mediante el plugin PouchDB Dump Cli.
  • En el código actual tenemos un estado llamado Splash donde podemos ejecutar todas estas acciones  de inicialización antes de entrar a la aplicación.

Sobre las vistas:

  • Las vistas en PouchDB se generan la primera vez que las llamamos, por lo que pueden demorar un poco mas de lo normal durante la primera vez. Ante esto, pueden controlar que la vista se genere de forma automática sobre todo durante la primera sincronización mediante la siguiente línea:
    pouch.query('vista', {stale: 'update_after'});
  • Hasta ahora hemos usado Map/Reduce para las vistas, y puede ser algo confuso. La alternativa principal es utilizar los índices, no hay nada mas simple y más rápido. La otra alternativa es utilizar PouchDB Find, que es el futuro de las consultas en todo el ecosistema CouchDB. Personalmente, aun me gusta más Map/Reduce aunque sea algo confuso y limitado por una razón: PouchDB Find es simplemente una máscara que hace las cosas fáciles, pero no agrega velocidad.
  • Finalmente, ya les había mencionado que las vistas ocupan sitio

Sobre los límites de espacio:

  • PouchDB maneja varios métodos para almacenar los datos siendo WebSQL (sqlite) y IndexedDB los disponibles en los móviles, estando igualmente sujetos a las restricciones de espacio del navegador del sistema. Aquí pueden revisar los límites . Un truco es utilizar el plugin SQLite que les permite saltarse esos límites y tener un almacenamiento casi ilimitado. Entiendan que esto es solamente recomendado cuando su base de datos esté por los 50 mb. Además, aún tiene algunos problemitas con las vistas.
  • IndexedDB es la alternativa estándar, de hecho WebSQL ya no es mantenido. De hecho es bastante rápido, además que IndexedDB no es tan fácil de abrir que WebSQL, así que les da un nivel de seguridad a los datos.

Sobre la seguridad:

  • Los que utilizan SQLite en Android están acostumbrados a ponerle una clave a la base de datos para protegerla. Esto no está disponible en el modelo Híbrido pero pueden usar el plugin Crypto Pouch que nos permite encriptar los datos.
  • CouchDB tiene usuarios y Cloudant también puede manejar algo parecido. Pueden acceder a esta lista de usuarios mediante el plugin PouchDB Authentication. Recuerden que esto significa que la red es necesaria. Personalmente, prefiero hacer un servicio externo que autentique y que distribuya las key y pass de Cloudant.

Modelamiento:

  • Modelar en NoSQL no es igual que en Relacional, pero hay trucos. Personalmente, prefiero el modelo minimalista de NoSQL pero existe un plugin Relational Pouch que permite utilizar la misma estructura relacional

Finalmente, como verán en algún comentario que hice, pueden tener toda su lógica en la aplicación cliente, siempre y cuando se quieran pegar al modelo “Offline-First”, pero si es que necesitan operaciones en línea, necesitaran de una capa más de lógica.

NOTA Principal: El problema principal con CouchDB y obviamente con Cloudant, es como integrarlo con nuestras bases en SQL. Es un trabajo en progreso en realidad, y no hay una herramienta porque en CouchDB el modelamiento está en función de la velocidad y de la aplicación que va a utilizar los datos, y eso puede ser muy diferente a lo que tenemos en SQL donde se programa para tener consistencia. Hay tantos modelos y tantas transformaciones posibles que una herramienta no puede hasta ahora hacer el trabajo. Felizmente, no es tan complicado porque ya en la base de datos hemos usando db.changes() , Así que el flujo sería:

  • Lenguaje recomendado: Python ( tiene una gran librería de acceso a Cloudant). También pueden usar NodeJS
  • Un script accede a la base de datos SQL y crea los documentos en Cloudant uno por uno según el modelo que hayamos fijado. Podemos cruzar dos tablas para crear documentos Maestro-Detalle siempre que no sean muchos detalles. No es tan rápido y lo ideal es usar BulkDocs para insertar por lotes.
  • Luego se invoca el comando db.changes()  donde cada documento cambiado es actualizado a la base de datos. Otra vez, puede ser como un insert, delete o update, todo depende de como sea su modelo.

Hasta ahora, todo esto es a medida por lo que mejor es que practiquen sus habilidades en NodeJS o Python.


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.

Me he tomado algo de tiempo para escribir este post por una simple razón: es hora de limpiar el código. Hasta ahora el código que han ido desarrollando es funcional pero tiene cosas que les va a crear problemas cuando quieran hacer algo mas grande. Lo bueno en todo esto es que si han ido cambiado el código desde el post 1, entonces estos cambios les harán mucho sentido. La buena noticia en todo esto, es que puedo publicar todo el código sin problemas.

Comencemos con el archivo app.js:


angular.module('starter', ['ionic','controller','service'])
.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();
};
});
})
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app',{
cache:false,
abstract: true,
views:{
"home":{
templateUrl:"templates/menu.html",
controller:"menuController as menu"
}
},
resolve:{
cats: function(db){
return db.getCats();
}
}
})
.state('app.news', {
cache:false,
url: "/:catId",
views: {
"menuContent":{
templateUrl: "templates/news.html",
controller: "newsController as news"
}
},
resolve:{
noticias:function($stateParams,db){
return db.getNews($stateParams.catId);
}
}
})
.state('app.detail', {
cache:false,
url: "/detail/:id",
views: {
"menuContent":{
templateUrl: "templates/detail.html",
controller: "detailController as detail"
}
},
resolve:{
noticia: function($stateParams,db){
return db.get($stateParams.id);
}
}
});
$urlRouterProvider.otherwise('/');
})

Empecemos poniendo atención en la definición de los estados a partir de la línea 14.

  • Si tenemos un estado que muestra datos que cambian muy seguido en la base de datos, lo mejor es desactivar el cache. Eso lo hacemos mediante el atributo “cache:false” como pueden ver en la definición de cada estado.
  • Los datos es mejor cargarlos en la definición del estado. Esto se traduce en la utilización del atributo “resolve” donde verán que hacemos la llamada al servicio que recupera los datos y los pasa mediante una variable al controlador. Podrán ver que un estado puede recibir parámetros y estos nos sirven para recuperar los datos llamando a los servicios.
  • $ionicPlatform.ready() es muy importante. Conforme nuestro proyecto crezca, notarán que hará falta inicializar algunos componentes o plugins de Cordova. Cordova es el core de todo esto y la noticia es que se demora en cargar, por lo que si tenemos que inicializar algún componente o plugin propio de Cordova tienen que ponerlo dentro de esta función pues se ejecuta cuando ya es un hecho que Cordova ya está cargado.

Ahora nos toca ir a revisar el archivo controller.js


angular.module('controller',['service'])
.controller('menuController',
['$scope',
'$state',
'cats',MenuController])
.controller('newsController',
['$scope',
'$state',
'noticias',NewsController])
.controller('detailController',
['$scope',
'$state',
'noticia',DetailController]);
function MenuController($scope,$state,cats){
$scope.cats = cats.rows;
};
function NewsController($scope,$state,noticias){
$scope.notas=noticias.rows;
$scope.$on('db:changed',
function(event,changed){
$state.go('.', null, { reload: true });
console.log('Recargando noticias');
});
};
function DetailController($scope,$state,noticia){
$scope.event= noticia;
$scope.$on('db:changed',
function(event,changed){
$state.go('.', null, { reload: true });
console.log('Recargando Noticia');
});
};

Aquí si hay varias cosas para revisar. Primero, si bien no hay reglas a la hora de programar, si existen buenas prácticas y la mejor que he encontrado es esta: Angular Style Guide, así que apliquemos algunas reglas.

  • La primera es lo que se llama IIFE. Considerando que el app.js es el archivo principal, tanto controller.js como service.js deben tener todo el código contenido en una estructura como esta:
    • (function(){ … })(); En nuestro caso, hemos incumplido esto para simplificar la explicación, pero pueden agregarlo y verán que el código funciona sin problemas.
  • Verán que las definiciones de cada controlador ahora es algo diferente y es por dos razones:
    • Dependency injection: Esto es casi un formalismo, es decir, la podríamos saltar sin problema pero nos costará cuando usemos herramientas para comprimir nuestro código o para cuando querramos empaquetar nuestro código para evitar copiones. Si se fijan el primer controlador, verán que primero indicamos todos los componentes, entre comillas, que vamos a utilizar dentro del controlador, y luego, tenemos una función como parámetro final sin comillas. Finalmente la función es definida al final incluyendo como parámetros todos los componentes que vamos a usar ya sin comillas.
    • Siguiendo las sugerencias de estilo, el código de cada controlador es ahora puesta en una función aparte a fin de facilitar la lectura del código
  • La base de datos puede cambiar. Para esto tienen que pensar mucho en como diseñar sus estados. En cada controlador he incluido un $scope.$on para recibir el evento $broadcast con el nombre ‘db:changed’ que ya veremos exactamente cuando se genera pero básicamente es que cuando se detecte un cambio en la base de datos debemos refrescar los datos, en nuestro caso, hemos elegido volver a correr el estado mediante la línea $state.go() que vuelve a correr el código.
    • Algo es super importante: Cuando refrescan un estado, el estado padre no se refresca, pero si vemos bien en nuestro caso, el controlador del estado padre no tiene nada de código para refrescar. La clave aquí es que el estado padre se trata de un estado abstracto y estas si se refrescan junto con los estados hijos. Es por eso que basta con refrescar los estados hijos.

Nota final, hay casos en los que su vista no requiera refrescar datos a cada rato, así que evalúen activar el cache o que el refresco de los datos venga de algún evento como un click en algún botón, o un cambio de estado. Refrescar automágicamente cuesta.

Ahora veamos el service.js


angular.module('service',[])
.factory('db',['$q','$rootScope',DbService]);
function DbService($q,$rootScope){
var key = 'bentareadyessharyinessee';
var pass = 'OnEixgKgpt8LyEtl0S5DkAon';
var remote = 'https://'+key+':'+pass+'@supermio.cloudant.com/news';
var db;
function changed(change){
console.log('cambios en la base de datos');
$rootScope.$broadcast('db:changed',change);
};
return {
init: function(){
if (!db) db = new PouchDB('news');
this.replicate();
return true;
},
replicate: function(){
if (!db) this.init();
db.replicate.from(remote,
{live:true,
retry:true})
.on('paused',function(changes){
changed(changes);
});
},
get: function(id){
if (!db) this.init();
return db.get(id);
},
getCats: function(){
if (!db) this.init();
return db.allDocs(
{startkey:'cat_',
endkey:'cat_\uffff',
include_docs:true});
},
getNews: function(catId){
if (!db) this.init();
if (catId)
return db.query('news/topic',
{key:[catId],
include_docs:true,
descending:true});
else return db.allDocs({startkey:'news_\uffff',
endkey:'news_',
descending: true,
include_docs:true});
}
}
};

  • Cuiden la replicación.
    • En la línea 20 verán que iniciamos la replicación  y con el atributo “live” y esto nos garantiza que los cambios se pasan casi instantáneamente pero como todo en la vida, tiene costo, así que consideren tenerlo apagado y encenderlo cuando haga falta. El siguiente atributo “retry” se refiere a que la replicación continúe luego de una falla en la red, tenganlo siempre en true, pero no se confíen.
    • Al encender la replicación podemos especificar eventos. En nuestro caso en la línea 23 encendemos el soporte del evento “paused” que se refiere a que la replicación “live” ya no recibe nuevos cambios, así que podemos decir que la base de datos remota y local son iguales. En nuestro caso, este es un buen momento para refrescar las vistas. Hay otro evento que es “change” que se genera con cada cambio que se recibe, lo cual es bueno pero no tanto porque si refrescamos aquí, tendríamos ejecutando el refresco muchas veces, sobre todo para la corrida inicial y nuestra base de datos local está vacía. Luego utilizaremos estos eventos para crear notificaciones locales.
  • Cuídense de los procesos asíncronos. Todos los métodos en el servicio “db” son iguales en una cosa: todos son asíncronos. Tal como les recomendé, usen los estados en el archivo app.js para llamar a estos métodos y se librarán de la pesadilla de javascript llamada “Promesas”. Ya luego veremos como manejar promesas.

Y listo, todos los demás archivos van iguales y en lineas generales podrán ver que el código está más limpio y ordenado, además, estamos llamando todos los datos directamente desde la base de datos.

Nota final: en lo que se refiere a performance, el consejo básico es que diseñen su app para que no tenga que refrescar automáticamente nada de nada, a menos que sea absolutamente necesario por la simple razón que si bien PouchDB es una buena base de datos, puede volverse bastante pesada si tienen que lidiar con una gran cantidad de datos. Refrescar datos por eventos es la mejor estrategia.

Como podrán ver, no han habido nuevas funciones que hayamos incluido, pero al menos hemos mejorado el código y espero que los consejos y el estilo de programación mostrado aquí les sirva para organizar su código en algo que puedan mantener y entender con facilidad.


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.

Bien, con lo desarrollado en la parte 07 ya tenemos una aplicación funcional que filtra los datos por categoría, pero no está completa pues faltaría una forma de poder elegir la categoría mediante un menú. En Ionic Framework, tenemos una librería de controles interesantes, y para nuestro caso, vamos a utilizar el side menú. Para eso tenemos que hacer la introducción de un concepto nuevo: estado abstracto.

Un estado abstracto es un estado utilizado para cuando lo que se quiere hacer es mostrar una parte del UI que es común para otros estados. Pensemos en que un estado abstracto es un marco donde podremos poner logos y cabeceras, e incluso poner cierta lógica y que tiene una zona donde otros estados podrán mostrar información.

Primero, comencemos cambiando nuestra configuración de estados en el archivo app.js:

.config(function($stateProvider, $urlRouterProvider) {
    $stateProvider
        .state('app',{
            abstract: true,
            views:{
                "home":{
                    templateUrl:"templates/menu.html",
                    controller:"menuController as menu"
                }
            }
        })
        .state('app.news', {
            url: "/",
            views: {
                "menuContent":{
                    templateUrl: "templates/news.html",
                    controller: "newsController as news"
                }
            }
        })
        .state('app.detail', {
            url: "/detail/:id",
            views: {
                "menuContent":{
                    templateUrl: "templates/detail.html",
                    controller: "detailController as detail"
                }
            },
            resolve:{
                detail: function($stateParams){
                    return $stateParams.id;
                }
            }
        });
        $urlRouterProvider.otherwise('/');
    });

Como pueden ver, hemos agregado un estado en la línea 17 donde el primer atributo es uno nuevo: abstract:true, lo que significa que éste será nuestro estado abstracto que servirá para colocar el side menu.
Una vez que hemos agregado el estado abstracto, ahora hay que indicar a los otros estados que deben tener como estado “padre” al estado “app”. Para eso, simplemente le añadimos “app.” al nombre de cada estado, con eso basta para indicar la dependencia. Fijense como ha cambiado el nombre en la línea 26 y en la línea 35.
Otro punto importante que deben notar es que hemos cambiado el valor del atributo “views” en la definición de cada estado en las líneas 29 y 38. Inicialmente, habíamos indicado que el estado sea mostrado en la vista “home” ahora le estamos diciendo que utilice la vista “menuContent”, puesto que estos estados serán mostrados dentro del estado abstracto, tendremos que indicar una vista definida dentro del estado abstracto. Veremos esto más claro al definir la vista para el menú.

En la configuración de estados hemos indicado que hay un controlador para el estado abstracto, así que agreguemos el controlador al archivo controllers.js:

angular.module('controllers',['services'])
	.controller('menuController',function(){
	})

Por ahora lo mantendremos así sin código.
Ahora agreguemos un template para el menú que hemos definido como el archivo menu.html

<ion-side-menus enable-menu-with-back-views="true">
    <ion-side-menu-content>
        <ion-nav-bar class="bar-stable">
            <ion-nav-buttons side="right">
                <button class="button button-icon icon ion-android-exit " ng-click="salir()"> Salir
                </button>
            </ion-nav-buttons>
            <ion-nav-buttons side="left">
                <button class="button button-icon button-clear ion-navicon" menu-toggle="left">
                </button>
            </ion-nav-buttons>
        </ion-nav-bar>
        <ion-nav-view name="menuContent"></ion-nav-view>
    </ion-side-menu-content>

    <ion-side-menu expose-aside-when="large" side="left">
        <ion-header-bar class="bar-stable">

<h1 class="title">Noticias</h1>

        </ion-header-bar>
        <ion-content>
<div align="center">
                <img width="50" src="img/ionic.png">Categorias
            </div>
            <ion-list>
                <ion-item nav-clear menu-close>
                    Nacional
                </ion-item>
                <ion-item nav-clear menu-close>
                    Internacional
                </ion-item>
                <ion-item nav-clear menu-close>
                    Espectáculos
                </ion-item>
                <ion-item nav-clear menu-close>
                    Opinión
                </ion-item>
            </ion-list>
        </ion-content>
    </ion-side-menu>
</ion-side-menus>

El componente sidemenu tiene dos partes: el side-menu y el side-menu-content, en nuestro ejemplo, hemos definido primero el side-menu-content donde podrán ver en la línea 13 que hemos definido la vista “menuContent” donde se mostrarán los demás estados dependientes.
En la línea 16 hemos definido el side-menu que es el menú propiamente que aparecerá a un costado. En este caso hemos definido que el estado se muestre a la izquierda “side=left” y que si la pantalla es muy ancha entonces el menú se muestre desplegado por defecto, si la pantalla es angosta, el menú se esconderá a la espera que hagamos un “swipe” o hagamos click en el botón de tres líneas que aparecerá.

Para resumir, tenemos ahora varios niveles de estado:
– Index.html define la vista “home”
– En la configuración de estados, pintamos el estado abstracto “app” definido en el archivo “menu.html” en la vista “home”
– Menu.html define la vista “menuContent”
– Todos los demás estados se hacen dependientes del estado app y serán mostrados en la vista “menuContent”.

El resultado es algo parecido a esto:
Con la pantalla ancha tipo tablet.

menuA

Con la pantalla angosta

menuB01 menuB02

Si hacemos click a una noticia podemos ver como se sigue mostrando dentro del mismo marco. También notarán que la lista de categorías está puesta manualmente y además no hacen mucho. Para no extender mucho el post, primero hacemos que el menú funcione.

Primero revisemos el estado original de nuestro servicio que controla los datos (services.js)

angular.module('services',[])
.factory('db',function($rootScope){
    var key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
   var pass = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
   var remote = 'https://'+key+':'+pass+'@server.cloudant.com/news';
   var db;
   var mostrar = function(){
        db.allDocs({startkey:'news_\uffff',endkey:'news_',descending: true,include_docs:true})
                    .then(function(result){
                    $rootScope.$broadcast('refrescar',result.rows);
                });
    };
    var mostrarCat = function(catId){
        db.query('news/topic',{key:[catId],include_docs:true,descending:true})
                    .then(function(result){
                    $rootScope.$broadcast('refrescar',result.rows);
                });
    };
    return {
        init: function(){
            if (!db) {
                db = new PouchDB('news');
            }
            mostrarCat("cat_01");
            this.replicate();
        },
        replicate: function(){
            db.replicate.from(remote,{live:true,retry:true})
                .on('paused',function(info){
                mostrarCat("cat_01");
            });
        },
        get: function(id){
            return db.get(id);
        }
    }
});

NOTA: En la línea 14 estoy incluyendo una corrección : he cambiado el parámetro “startkey” por “key”. La diferencia es que “startkey” se usa para cuando se quiere establecer un rango, por lo que “cat_01” con el parámetro descending true devolverá sólo “cat_01”, mientras que “cat_04” mostrará también “cat_03”, “cat_02” y “cat_01” lo que no buscamos. Con “key” solamente se devolverán los valores iguales al id de categoría.

Podemos ver que estamos pasando “en duro” la categoría 1 como parámetro para mostrar, por lo tanto tenemos que crear una variable miembro para almacenar este valor, cosa que al seleccionar un elemento del menú su valor se cambie para mostrar las noticias correspondientes. Por defecto mostraremos todas las noticias y tendremos que agregar un método para actualizar la variable de categoría y refrescar la vista. En el archivo services.js, empecemos agregando la variable cat:

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

Luego agregamos el método que llamaremos “setCat”

get: function(id){
    return db.get(id);
},
setCat: function(id){
    cat = id;
    mostrarCat(cat);
}

Ahora, tenemos que eliminar las llamadas de mostrarCat con la categoría “en duro”

return {
    init: function(){
        if (!db) {
            db = new PouchDB('news');
        }
        mostrarCat(cat);
        this.replicate();
    },
    replicate: function(){
        db.replicate.from(remote,{live:true,retry:true})
            .on('paused',function(info){
            mostrarCat(cat);
        });
    },

Finalmente, si mostrarCat recibe un valor nulo o vacio debemos mostrar todas las noticias.

var mostrarCat = function(catId){
    if (catId)        
    db.query('news/topic',{key:[catId],include_docs:true,descending:true})
                .then(function(result){$rootScope.$broadcast('refrescar',result.rows);});
    else mostrar();
};

Con eso ya arreglamos el modelo, ahora vamos al controlador en el archivo controllers.js.

angular.module('controllers',['services'])
	.controller('menuController',function($state,$scope,db){
		$scope.setCat = function(id){
			$state.go('app.news');
			db.setCat(id);
		};
	})

Aquí estamos usando una nueva entidad $state que nos va a permitir llamar a un estado.
Ahora, en la vista tenemos que llamar a la función del controlador al momento de hacer click. Como tenemos las categorías “en duro”, seguiremos indicando las claves “en duro”, así que modificamos el archivo menu.html para incluir los links que llamen a la función $scope.setCat(id):

 <ion-list>
     <ion-item nav-clear menu-close ng-click='setCat("cat_01")'>
       Nacional
     </ion-item>
     <ion-item nav-clear menu-close ng-click='setCat("cat_02")'>
       Internacional
     </ion-item>
     <ion-item nav-clear menu-close ng-click='setCat("cat_03")'>
       Espectáculos
     </ion-item>
     <ion-item nav-clear menu-close ng-click='setCat("cat_04")'>
       Opinión
     </ion-item>
</ion-list>

Lo que hemos hecho es agregar la propiedad ng-click para que se ejecute la funcion setCat(id) definida en el controlador tal como lo dijimos.

Y listo. Por ahora tendremos el siguiente comportamiento:

  • La aplicación mostrará todas las noticias al cargar
  • Al hacer click en una categoría, se mostrarán solamente las tareas relacionadas

En el siguiente post veremos como cargar las categorías directamente de la base de datos y ya no “en duro”. Ya saben si encuentran algún error, me avisan.


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.


Acaba la presentación de Apple y simplemente se extraña cuando cada anuncio significaba un gran salto en diseño y las ganas de formar lineas a las afueras de un Apple Store. Esta vez no es así, de hecho hace varios eventos que ya no se nota esa adrenalina que solía emanar Steve. Puede ser que la creatividad en un solo producto tiene puntos altos y puntos bajos, y con iPhone ya se alcanzó lo más alto asi que lo mejor que pueden hacer es mantenerse o tratar de caer muy lento.

Donde se nota un retroceso es en la introducción de un iPhone 5C con un precio menor tal como se hacía con los iPods nano y shuffle con tal de abarcar nuevos mercados donde el precio es clave, algo que le quita a la línea iPhone su sentido de exclusividad. Ahora, con un case el 5c y el 5s son exactamente iguales y dada la calidad de los operadores en la región, la velocidad tampoco los va a diferenciar. La clave podría ser que las personas con un 5s dejarán de usar cases sólo para mostrar que tienen uno que no es de plástico. Y los features?

Al parecer Apple, le ha perdido la fe a su propio teléfono así que ahora apunta a precios menores para no afectar su volumen de ventas, porque ya quedó claro que su mercado no compra en función a los features, compra por la experiencia por ser parte de algo mas grande que ellos y que hasta hace poco era exclusivo o les daba la impresión de serlo. Todavía hay hipsters wannabes que no podian comprar un iphone pero que ahora lo podrán hacer.

El sistema operativo iOS7 viene bien pero no es lo que los usuarios quieren ver, quieren algo de magia. Tal vez haga falta que Apple lance un teléfono que se transforme o algo por el estilo para recuperar su aura, porque con mas procesador, memoria o nuevos gráficos no va a volver a atrapar al mundo y volverlos en casi zombies. Quizá la clave esté en la duración de la batería, algo que ya han hecho con el macbook Air que ya estaba bien y que ahora con su batería que dura unas increíbles 12 horas simplemente ha dejado la valla muy alta para las demas. Un teléfono que dure 12 horas sería simplemente romper con todos, aún cuando se mantenga la misma apariencia.

Alguién le debe hacer notar esto a Apple.


En Barcelona 2013, la novedad es que regresamos a lo básico. Teléfonos mas simples donde se busca la funcionalidad básica y obviamente un mejor precio.

Para los que usamos smartphones, es bueno preguntarnos de vez en cuando lo siguiente: ¿Aprovechamos todas las funciones de nuestros teléfonos? Es decir, ¿Valió la pena pagar el precio adicional por todas las funciones que venian en el modelo top? Realmente utilizamos todo eso?

Estuve revisando las funciones adicionales del Samsung S3, acercar para llamar, pantalla de inicio adicional en Roaming y para cuando conectas tus audífonos, comandos de voz y todas esas cosas que realmente he usado 2 o 3 veces y luego desactivé porque consume batería.

Ahora vean sus teléfonos y háganse la misma pregunta. ¿Cuántas de las funciones por las que pagaron de más realmente son útiles? Si se compraron el teléfono como un accesorio de moda entonces dejen de leer, ustedes sigan felices con un teléfono que es parte de su guardarropa.

Nokia ya no es el gran fabricante de teléfonos high end que todos queriamos, ahora está luchando por sobrevivir, y realmente lo que está haciendo es asegurar el sector de mercado donde siempre fue líder: el segmento low. En lugar de resignarnos con marcas chinas, ahora tendremos Nokias, que por un poco mas de dinero pero no tanto, nos ofrecerá un rendimiento básico y más confiable.

Nokia hace muy bien lo que los smartphones tienen débil, calidad de voz, duración de la batería, resistencia a los golpes, y demás cosas que definitivamente si usamos en nuestros teléfonos el día de hoy.

Si comparamos esta estrategia con la de Blackberry, vemos que ellos se han lanzado a competir directamente con Android, iPhone y todos los demás en el segmento high. Es decir, les doy algo parecido a Android, iPhone a mis aún clientes para que no se vayan, y a aquellos que siempre estuvieron interesados en Blackberry. Los grandes olvidados son los usuarios corporativos que aún estan con Blackberry por Bis/Bes y sus teléfonos con QWERTY.

Nokia va por la recuperación del segmento donde siempre fue líder y no me queda mas que saludar la decisión. Así que si tienen que comprarle un teléfono fácil de usar y a prueba de balas a algún familiar busquen algún Nokia en el catálogo.


Hay varios modelos de smartphones que me interesan:

– HTC One X, por su integración de la tecnología Beats Audio

– Galaxy S3 por su procesador de 4 núcleos

iPhone 5 para probar mis aplicaciones en una pantalla mas grande

Motorola Razr HD Maxx, porque el kevlar siempre luce bien y por su super batería

Nokia Lumia 920, para ver que tal luce ahora con WP 8 y con la tecnología Pureview para las fotos

 

No puedo tenerlos todos, pero confío en mi suerte para tenerlos aunque sea para hacerles un test. Comprarlos, ni hablar. A fin de hacerles un test honesto, es necesario que vengan prestados o regalados.

En fin, la pregunta que surge inmediatamente después es ¿Cuál de estos teléfonos yo usaría para el día a día? o ¿Cuál podría yo recomendar?

La pregunta es difícil porque la verdad absoluta en esto es que no hay un mejor teléfono, hay una experiencia de uso para cada uno. En otras palabras, elegir un smartphone es tan igual que comprarse un par de zapatos. Ahora, como hay muchas cosas en los smartphones que no podemos cambiar, aquí les doy unos criterios para encontrar su propio teléfono ideal:

 

El mas importante: debemos tener bien claro, que tanto Internet nos hace falta. Identifiquemos las aplicaciones que quisieramos y veamos si es que nos justifica para comprar un smartphone. Comprar un smartphone solo por tenerlo es un error gigante. Si nuestro interés mayor son las llamadas de voz (que todavía habemos muchos) hay alternativas muy elegantes.

Puede ser que nuestro interés esté por el lado de la mensajería, ya sea instantánea o email, entonces revisaremos que el smartphone sea cómodo para escribir. Puede que nos sirva uno de pantalla pequeña.

De repente, queremos simplemente navegar en Internet o acceder a redes sociales, entonces una pantalla de buen tamaño es lo ideal.

El form factor: la forma del smartphone. Una pantalla grande es buena, pero significa que tendremos que evitar quejas cuando lo tengamos en el bolsillo, igual cuando tenemos uno pequeñito que se lleva bien en cualquier parte pero que no nos deja ver bien las páginas o aplicaciones.

Si ya estamos claros con el criterio anterior, ahora tenemos que empezar a probarnos los zapatos. Si elegimos una pantalla grande, tenemos que ver si vamos a estar dispuestos a usar el equipo, ya sea por su peso o forma. Incluso aquí tenemos que ver la parte estética del aparato. Algo que vaya con nuestro estilo y comodidad, ambas cosas siempre de la mano.

– Los juguetes: Que cositas extra vienen con el smartphone. Aquí tendremos que revisar las funciones que no son tan críticas pero que marcan diferencias entre los smartphones, tales como la calidad de la cámara digital, mapas, sensores, interacción por voz y todas esas otras cosas que vienen por ahí. NO DETERMINEN SU ELECCION POR ESTOS FACTORES A MENOS QUE REALMENTE ALGUNA DE ESTAS FUNCIONES SEA SU INTERES PRINCIPAL.

Finalmente, un factor que siempre queda afuera pero se relaciona con todos estos criterios: la duración de la batería.

Sobre este aspecto tenemos que decir que cada función adicional que tenga el smartphone que escojamos significará un golpe para la batería así que debemos elegir bien lo que usamos si es que no queremos estar buscando una toma de corriente cuando estamos en la calle. Mientras mas usemos los “juguetes” del último criterio, menos nos durará la batería.

Si estamos en un paseo un día sábado y nos ponemos a tomar fotos con el celular, no habrá problema llegar a casa luego y recargar la batería. Pero si compramos el celular para contestar emails urgentes, no nos vamos a poner a tomar fotos en mitad de la semana y justo a la hora del almuerzo, sabiendo que la mayor carga de correos urgentes llegan como a las 5pm.

No hay una misma experiencia de uso para todos, y las funcionalidades de un teléfono no hacen la experiencia. La experiencia son las aplicaciones que necesitamos usar mientras estamos en la calle.


Con la salida del iPhone al mercado se ha marcado una tendencia clara en lo que se refiere al diseño de los celulares y esta es que mientras mas grande y nítida sea la pantalla, mejor lo que hace que los teclados físicos sean cosa del pasado.

Incluso, uno de los abanderados de los teclados QWERTY en móviles como RIM simplemente se rindió ante esta tendencia y sigue terco con sus modelos Storm, aunque siga siendo el modelo Bold el màs requerido.

¿Es que acaso el teclado ya no tiene sitio en los móviles? parece que si tiene un sitio, de lo contrario RIM si estaría muerto, o no habrían Androides con teclado, aunque no sean los que mas venden.

¿Acaso estaremos dejando la practicidad de un teclado QWERTY por la factor Sexy de un telefono full pantalla? Me parece que si aunque no tenga cifras para probarlo. Deberìa haber un estudio para medir si es que los que utilizan un teléfono full pantalla escriben menos como es que sospecho. Lo que si es muy cierto es que las personas que realmente trabajan con su teléfono y utilizan para eso el correo electrónico, no tienen una mejor alternativa que el Blackberry con teclado QWERTY. Es imposible lograr la misma productividad con otro modelo de teléfono.

No puedo encargar estudios sobre comportamientos así que esta vez simplemente dejaré mi tesis: Los usuarios de teléfonos full pantalla suelen basar su comunicación en fotos o frases cortas, mientras que los usuarios de teléfonos con teclado QWERTY simplemente no tienen límite.

Si revisamos la lista de equipos más vendidos veremos que todos tienen casi el mismo diseño: un full pantalla. ¿Es realmente este el diseño para ser mas productivos? Depende de nuestras necesidades como siempre, pero cada día parece que somos empujados a elegirlos porque hay cada vez menos alternativas.

Veamos las alternativas:

– Blackberry sigue teniendo fuerza gracias a su diseño candy bar con qwerty y que es rescatado por algunos smartphones como HTC con su Status. Pero con una diferencia. Los que utilizan Blackberry notaran que el sistema operativo esta diseñado para extraer toda la utilidad del teclado, mientras que los que han utilizado algún Android con teclado, notaran que el teclado suele confundir al sistema operativo, o a nosotros por comportamientos erráticos, como si el teclado no haya sido considerado en el diseño del software.

– Las alternativas de escritura rápida como Swype que buscan aumentar la velocidad de escritura de los teclados en pantalla. A mi simplemente no me sirve.

– La resignación de escribir con errores o limitarnos a frases cortas.

Yo creo que la última opción nos esta ganando.

La mejor solución a esta situación es que exijamos mejores diseños que se adecuen a nuestras necesidades y no simplemente resignarnos a elegir algo de lo que hay disponible. Esta bien que iPhone sea el más buscado, pero hay gente que requiere un teclado o una alternativa igual o mas productiva.


Microsoft ha estado hace rato metido en la pelea de los smartphones con Windows Phone 7 pero deberíamos decir que recién hace su entrada a las grandes ligas con el lanzamiento de los telefonos Nokia Lumia 800 y 710, de hecho representa mucho mas que simplemente un competidor mas en el mundo de smartphones, es uno mas en la pelea de los ecosistemas.

Microsoft tiene una gran ventaja en el mundo móvil pues tiene el control sobre algunos elementos que muchos consideramos claves en algún determinado mercado. Veamos:

– Microsft Exchange, es algo así el estándar de “officeware” corporativo.

– Microsoft Office también es casi parte del estándar.

– Xbox Live, es la mejor red de juegos y entretenimiento (Sony podrá tener una mejor consola, pero con PSN aún sigue en deuda, y sobre juegos, me refiero a los de verdad y no a los tipo “iPhone” )

 

Puede que Microsoft haya llegado un poco tarde a la fiesta, pero se les podría perdonar si es que con “Mango” y Nokia hacen buen uso de estos tres elementos.

Pero veamos lo que realmente trae Windows Phone 7 y Nokia: una experiencia de uso  controlada por el  OS y un hardware “killer”.

Windows Phone 7 se diseñó bajo el concepto “Metro” y a eso se tienen que someter todas las aplicaciones. Además, Microsoft controla la apariencia y comportamiento de las “funciones básicas” del teléfono como son “la lista de contactos”, “los servicios de mensajeria”, “el manejo de la multimedia” y “el manejo de las redes sociales”.

La lista de contactos ya no existe mas, de hecho en WP7 esta lista es realmente lo que todos los OS anteriores han querido, una entrada única a todos los demás servicios. Desde la lista de contactos ya estoy dentro del mundo de las redes sociales, de la mensajería instantánea, del SMS y hasta el email y todo sin tener que abrir una nueva aplicación. En Android ya había algo así pero mas por acción de algún fabricante como HTC o Samsung que por el mismo Google aunque la cosa se mejora con su última version: Ice cream sandwich que aún no llega al mercado. Apple iPhone no tiene nada parecido y el diseño de iOS no lo permite además lo que significa que no va a tener respuestas en este aspecto por mucho tiempo, de todas maneras, acuérdense que los usuarios de iPhone se mueven diferente.

Los servicios de mensajería ya no son solamente un icono mas, ahora son un “hub” y además, están por todo lado en que ahora es el “hub” de contactos. En pocas palabras, la mensajería esta por todo lado. Ya no es una aplicación, es una característica de algunos objetos en el teléfono. Esto es algo así como empezar a usar las calculadoras HP, tenemos que empezar a pensar de otra manera: primero el destino, luego la acción y al final el medio de transporte cuando en otro teléfono hacemos todo esto al revés. Suena mas lógico y mejor, pero la costumbre a veces es mas fuerte que el amor.

La multimedia también es ahora manejada de forma distinta, pues no tenemos albumes o carpetas de fotos. Ahora todo está asociado o a un contacto, o a una red social o a algún tipo de accción que hayamos realizado por lo que el teléfono no pretende mostrarnos imagenes o música o videos, nos pretende recrear la experiencia alrededor de la misma.

Lo de las redes sociales es mas radical aún. El diseño del sistema operativo hace totalmente innecesario instalar un cliente Facebook o Twitter, pues sus funciones han sido convertidas en “características” del propio OS. Por ejemplo, un contacto en el teléfono puede ser un contacto en Facebook, así que en el teléfono lo veremos con sus fotos, sus mensajes, sus cambios de status, sus twitts y todo eso y si quiero interactuar con este contacto pues lo haré a través de las funciones de mensajería, multimedia que ya vienen en el sistema operativo. Es decir, no tengo que abrir otra aplicación pues ya todo está ahí. Y si alguien mas quiere entrar, digamos que LinkedIn, Orkut o  Google+, antes que un cliente tendría que crear una integración con el OS, tal como lo ha hecho skype.

En suma, hay que cambiarnos el cerebro para usar WP7 y al pacer, cambiarlo para bien. Porque con Metro estamos hablando que voy a seguir usando las mismas secuencias cada vez que tenga que enviar un mensaje sin importar su tipo (sms, mms, email, Facebook im, Windows Live, etc) porque hay una capa de servicios de interfase de usuario básicos que son controlados por el OS y que elimina la complejidad de un “cliente” que nos cambia la ubicación y la secuencia para todo. Es algo parecido a lo que hace Web OS con el Synergy pero llevado un paso mas alla. Esta es la mayor apuesta de Microsoft.

Y en lo que respecta al Hardware, por fin tenemos un fabricante serio al que podamos evaluar. El nombre de Nokia lo tenemos asociado a calidad en comunicación de voz y a faclidad de uso y esto no ha variado mucho con el tiempo. Un teléfono Nokia quizá no era el mas bonito pero era el mejor para hacer o recibir llamadas. Pues ahora ese valor esta en un Smartphone, es decir en un aparato que históricamente no ha sido un buen aparato para hacer o recibir llamadas, así que estamos hablando del primer Smartphone que es un buen teléfono para llamadas. En esto no pueden fallar. Es seguro que no tendremos ningún antennagate ni nada por el estilo, y quisieramos ver que tan bien nos rinde la batería.

Entonces, en suma tenemos un buen hardware para el teléfono y un sistema operativo que nos empuja a pensar las cosas diferente y que hay muchas probabilidades que sea un “diferente” bueno.  Creo todavía que WP7 está algo tarde en el negocio y que su oportunidad está en los form factor es lo que Nokia puede aportar en este aspecto además de la seguridad de tener por fin un smartphone que además sea un buen teléfono.

 


Hoy salió a la luz una carta abierta de un empleado de RIM (http://www.engadget.com/2011/06/30/rim-gets-handed-open-letter-from-disgruntled-employee-quickly-r/) donde se dicen las mismas cosas que todos hemos estado hablando desde hace ya bastante tiempo.

La carta dice básicamente:

  • Blackberry OS ya no da mas. Sugiere migrar a QNX y yo recomendaba Symbian. Desde mi punto de vista el cambio de OS también debe suponer también un rediseño del hardware.
  • El desarrollo de software interno no es rápido. De eso ya nos dimos cuenta
  • Blackberry ha estado lanzando productos incompletos. A la mente viene el Blackberry 9800 que no llegó a llenar las expectativas de nadie. Una pena que la mayor innovación en años de RIM sera un form factor introducido por HTC hace años.
  • El ecosistema de desarrolladores ha generado programas que no han llegado a cautivar a los usuarios, lo que no sucede con iPhone y Android.
  • Hace falta un golpe de innovación para recapturar a los usuarios. Otro ejemplo, un Bold 8300 tenía una duración de batería de hasta 3 días mientras que el Bold 9000 duraba apenas la mitad del tiempo, luego sale el Bold 9700 que es mas del mismo form factor. Y el anunciado Bold 9900 es el mismo form factor. Vaya creatividad.
  • Un cambio de mentalidad es necesario, sobre todo no creerse lo máximo.
  • Blackberry es el líder pero ya se están acercando los demás.
  • No se promueve la iniciativa internamente
No hace falta hacer una auditoria interna para darnos cuenta que todo eso hace falta en RIM, entonces, ¿Porqué tanta demora en aplicar un cambio?
Aún sigo convencido que el mayor recurso de RIM es su plataforma de mensajería y que su punto débil está en sus terminales así que desde mi punto de vista el cambio viene por ahí. Cómo se organicen internamente es algo que va mas allá del tema tecnológico.
¿Qué hace falta para que RIM reaccione? No lo se. Si leemos la respuesta de RIM (http://blogs.blackberry.com/2011/06/rims-response-to-open-letter/) nos quedamos sin respuestas.




%d bloggers like this: