Publicado por Kobi Glick, administrador de producto, Google Play

Publicar una app nueva, o una actualización para una app, es un hito importante y emocionante para cada desarrollador. A fin de que el proceso sea más uniforme y controlable, anunciamos el lanzamiento de una nueva manera de publicar apps en Google Play con funciones nuevas. Los cambios te darán la posibilidad de administrar las versiones de tus apps con más confianza a través de una nueva página de administración de versiones en la Google Play Developer Console.




Administra las actualizaciones de tus apps de manera clara y controlada

En la nueva página de administración de versiones, podrás actualizar versiones alfa, beta y de producción de tu app. En este espacio, puedes ver información importante y el estado de todas tus versiones en pistas.

Nueva página de administración de versiones.
Acceso más sencillo a funciones de publicación existentes y nuevas

Publicar una app o actualización es un paso importante y conveniente para todos los desarrolladores. A modo de ayuda, hemos agregado dos funciones nuevas.
En primer lugar, hemos agregado un paso de validación en el que se resaltan posibles errores antes de la publicación. La nueva página de “revisión e implementación” aparecerá antes de que confirmes la implementación de una nueva app e indicará si existen errores o advertencias de validación. Este nuevo flujo facilitará el proceso de lanzamiento de una app, en especial para apps que usen varios APK. También proporciona información; por ejemplo, en casos en los que hayas agregado nuevos permisos para tu app, el sistema lo resaltará.


En segundo lugar, ahora es más simple realizar y controlar implementaciones por etapas durante el flujo de publicación. Gracias a las implementaciones por etapas, puedes lanzar tu actualización para un porcentaje de usuarios que va en aumento. Esto te da la posibilidad de detectar y corregir cualquier error antes de que afecte a todo tu público.

Si deseas revisar el historial de tus versiones, ahora es posible realizar un seguimiento detallado de estas y descargar APK anteriores.

Por último, hemos agregado una nueva biblioteca de artefactos en la sección de administración de versiones, en la que puedes hallar todos los archivos que te permiten administrar una versión.
Comienza a usar hoy la nueva página de administración de versiones
Puedes acceder a la nueva página de administración de versiones en la consola para desarrolladores. Para obtener más información, visita el Centro de ayuda para desarrolladores de Google Play. Con estos cambios, te ayudaremos a publicar, controlar y administrar tu app con confianza en Google Play.


¿Cuán útil te resultó esta entrada de blog?
       



En una publicación previa del blog, presentamos el Centro de Políticas de Desarrolladores de Google Play. A fin de preservar la idea de hacer que las políticas sean más accesibles y útiles para los desarrolladores, el equipo de políticas creó un atractivo video con el propósito de ayudar a los desarrolladores a cumplir con la política de Google Play.

En una publicación previa del blog, presentamos el Centro de Políticas de Desarrolladores de Google Play. A fin de preservar la idea de hacer que las políticas sean más accesibles y útiles para los desarrolladores, el equipo de políticas creó un atractivo video con el propósito de ayudar a los desarrolladores a cumplir con la política de Google Play.



A continuación, se ofrecen 10 consejos para cumplir con la política de Google Play;

Repasa la información del Centro de Políticas: se te recomienda revisar el contenido del Centro de Políticas cuando no sepas con certeza si tu app infringe o no la política.

Describe tu app de manera apropiada: tómate un momento para describir tu app correctamente a fin de evitar infringir la política de metadatos. Recuerda que todas las traducciones de la descripción de tu app deben cumplir con la política de metadatos.

Usa imágenes para las cuales cuentes con derechos: en el ícono de tu app y en cualquiera de los recursos gráficos de tu listado de la tienda solo deben incluirse imágenes para cuyo uso tengas derechos. Si se te otorga permiso para usar recursos de otras personas, puedes notificar al equipo de revisión de apps con este formulario. Asegúrate de que el texto y las imágenes que uses en listado de tu listado de la tienda sean apropiados para aficionados a las apps de cualquier edad.

Clasifica tu app con precisión: al completar el cuestionario de clasificación de contenido, es importante proporcionar respuestas precisas a fin de recibir una clasificación precisa.

Administra los datos de usuario cuidadosamente: entre los datos de usuario puede incluirse información que este provea, o bien información recopilada que tenga que ver con él o con sus interacciones con la app o el dispositivo. Si en tu app se recopilan datos de usuario personales o confidenciales, deberás administrarlos de manera segura e incluir una política de privacidad en tu listado de la tienda y tu app.

Asegúrate de que los anuncios de tu app cumplan con la política: el comportamiento de los anuncios debe ser simple. Mostrar anuncios que generen interrupciones o sean engañosos va en contra de la política; se incluyen aquellos que se muestren como notificaciones del sistema, que no puedan cerrarse o que aparezcan cuando el usuario cierre la app. A su vez, en los anuncios de tu app no deben incluirse imágenes para adultos, contenidos violentos ni ningún otro elemento que pudiera ir infringir la política de contenidos restringidos. Probablemente te convenga consultar a tu proveedor de anuncios para conocer las opciones de filtrado.

No olvides la política de contenidos restringidos: Verifica tu app y tu listado de la tienda en busca de contenidos restringidos, como aquellos que estén destinados a adultos, exhiban violencia o hagan alusión al consumo de drogas. Si el contenido de tu app es motivo de preocupación para ti, lee la política de contenidos restringidos para obtener más información y hallar ejemplos. Si parte del contenido de tu app es generado por los usuarios, deberás tomar medidas de precaución adicionales a fin de ofrecer una experiencia que se adecue a la política. Para obtener más información, consulta la política de contenidos generados por el usuario.

Actualiza tus preferencias de correo electrónico: asegúrate de actualizar tu dirección de correo electrónico en la página de detalles de la cuenta de tu Consola para desarrolladores. De esta manera, si surge un inconveniente relacionado con la política podremos contactarte y transmitirte los pasos para corregirlo.

Corrige todos los inconvenientes que encuentres durante la revisión: aunque sometas tu app a una revisión conforme a las políticas, siempre es posible que la rechacen o suspendan después de este proceso. Si se rechaza tu app o la actualización de esta, ten en cuenta que muchas infracciones pueden corregirse. Sigue los pasos del correo electrónico con el asunto “Notification from Google Play” (notificación de Google Play) que recibiste.

Solicítanos asistencia: si no estás de acuerdo respecto del incumplimiento de una política, o si deseas recibir ayuda para corregir un inconveniente relacionado con una política, comunícate con nuestro equipo de asistencia sobre políticas.

En la siguiente publicación del blog, observaremos más nuestro proceso de cumplimiento y nuestros recursos de asistencia sobre políticas.

Hasta la próxima entrega, permanece atento a las actualizaciones de AdMob siguiendo nuestras páginas de Twitter, LinkedIn y Google+.

Publicado por Chris Jones, Equipo social, AdMob.




Debo ser sincero con todos: no haré una declaración de propósitos de año nuevo para 2017. Tampoco la hice en 2016. De hecho, creo que nunca hice una. No es que tenga visión oscura de las declaraciones de propósitos de año nuevo. Simplemente, creo que si hiciera una sería muy probable que la incumpliera en solo una semana y me sintiera mal por ello.


Debo ser sincero con todos: no haré una declaración de propósitos de año nuevo para 2017. Tampoco la hice en 2016. De hecho, creo que nunca hice una. No es que tenga visión oscura de las declaraciones de propósitos de año nuevo. Simplemente, creo que si hiciera una sería muy probable que la incumpliera en solo una semana y me sintiera mal por ello.

Algo que me resultó útil en el pasado fue considerar el año nuevo como un punto de inflexión para probar nuevas alternativas y también mejorar el trabajo que ya estoy haciendo. Para mí (y espero que para ustedes también), 2017 será un año propicio para optimizar la calidad de las apps.

La frase “calidad de las apps” puede tener muchos significados diferentes según los aspectos que se valoren en el software que se crea y se usa. Desde mi perspectiva como desarrollador, esto tradicionalmente implica corregir errores que ocasionan problemas para los usuarios. También podría reflejar el nivel de satisfacción que experimentan los usuarios de una app. Todo esto se reduce a la principal métrica con la que contamos para medir la calidad de una app móvil: su calificación en la tienda en la que se publica. Estoy seguro de que aquellos de ustedes que tengan una app en una vidriera han prestado mucha atención a la calificación de esta en algún punto.

Firebase proporciona algunas herramientas que pueden usar para optimizar la calidad de sus apps. Si aún no las usan, es posible que un panorama les resulte útil este año.

Firebase Crash Reporting

La herramienta más sencilla para dar los primeros pasos es Firebase Crash Reporting. Casi no necesitarán líneas de código para integrarla a sus apps de iOS y Android, y una vez que lo hagan la consola de Firebase comenzará a mostrar los bloqueos que experimenten sus usuarios. Con esto, obtendrán una “lista de resultados” con problemas que deberán solucionarse.
Como parte del equipo de informe de fallos, me resulta irónica la forma en que consideramos la entrada de bloqueos totales recibidos al monitorizar nuestro sistema. Al igual que con cualquier buen producto para desarrolladores, nos esforzamos por aumentar la adopción. Esto significa que celebramos cuando la proyección en un gráfico tiene dirección “ascendente y hacia la derecha”. Por lo tanto, en cierto extraño sentido, nos agrada ver más bloqueos porque esto indica que más desarrolladores usan nuestro producto. Pero para todos ustedes, los desarrolladores, una mayor cantidad de bloqueos es obviamente algo *negativo* y deben procurar que las cifras disminuyan. Por ello, eviten lo que hacemos nosotros: hagan que en 2017 las proyecciones en sus gráficos de informes de fallos de fallos tengan dirección descendente y hacia la derecha.

Presentación de Firebase Test Lab for Android

Aun mejor que solucionar un problema, cuando los usuarios lo experimentan, es evitar que este llegue siquiera a afectarlos. Para sus apps de Android, pueden usar Firebase Test Lab a fin de asegurarse de que estas ofrezcan un funcionamiento óptimo a sus usuarios para la creciente variedad de dispositivos reales que abarcamos. Tradicionalmente, ha sido muy difícil adquirir y abarcar una buena selección de dispositivos para pruebas. Sin embargo, con Test Lab simplemente deben cargarse los APK y las pruebas; la herramienta luego los instala y ejecuta en los dispositivos. Una vez que se completen las pruebas, proporcionaremos la totalidad de las capturas de pantalla, los videos y los registros de todo lo sucedido para que los examinen en la consola de Firebase.

Gracias a la disponibilidad actual de Firebase Test Lab for Android con amplias cuotas diarias sin costo para proyectos en el nivel de Spark, 2017 será un excelente año para poner manos a la obra. A su vez, si no han configurado sus compilaciones de apps de Android en un entorno de integración continua, podrían hacerlo y luego aplicar ajustes para que se ejecuten sus pruebas de manera automática en Test Lab.

Si pertenecen a ese grupo de personas a las que les agrada escribir pruebas para su código (ciertamente, no hay demasiados entre nosotros), lo natural será hacer que esas pruebas se ejecuten en Test Lab. Sin embargo, aquellos de nosotros que no mantengamos un conjunto de pruebas con nuestra base de códigos podemos usar la prueba de Robo de Test Lab para obtener cobertura de pruebas automatizada de inmediato, sin que se necesiten líneas de código adicionales. No es lo mismo que contar con un conjunto de pruebas abarcador. Por ello, es posible que 2017 sea un buen año para aprender más acerca de la creación de apps que pueden someterse a prueba y de la forma en que dichas prácticas pueden elevar el nivel de calidad para sus apps. Planeo redactar más artículos sobre esto posteriormente, durante el año. Permanezcan atentos a las novedades del blog de Firebase para obtener más información.

Firebase Remote Config

En su esencia, Firebase Remote Config es una herramienta que les permite configurar sus apps usando parámetros que se configuran en la consola de Firebase. Se puede usar para administrar la calidad de sus apps, y es posible aplicar algunos trucos estupendos con ella. ¡Tal vez este año haya nuevas oportunidades de probarlos!

En primer lugar, pueden usar la configuración remota a fin de implementar una nueva función para sus usuarios con los recaudos correspondientes. El funcionamiento es el siguiente:
  1. Codifiquen su nueva función y restrinjan el acceso a esta por parte del usuario a través de un parámetro booleano de configuración remota. Si el valor es “false”, sus usuarios no verán la función. Fijen este parámetro como valor predeterminado en la app.
  2. Configuren el parámetro en la consola de Firebase de modo que inicialmente también sea “false” para todos los usuarios.
  3. Publiquen sus apps en la tienda.
  4. Cuando sea el momento de implementar la nueva función para un segmento de usuarios pequeño, fijen el parámetro en “true” para el cinco por ciento de la base de usuarios (por dar una cifra).
  5. Permanezcan atentos a la presentación de nuevos bloqueos en Firebase Crash Reporting y también a los comentarios de sus usuarios.
  6. Si existe un problema en la nueva función, reviértanla de inmediato fijando el parámetro en “false” en la consola para todos los usuarios.
  7. Si el panorama es favorable, aumenten el porcentaje a medida que pase el tiempo hasta alcanzar el 100% de los usuarios.




Este método es mucho más seguro que el de publicar sus nuevas funciones para todos los usuarios con una sola actualización de las apps, ya que tienen la opción de desarticular de inmediato un problema serio, además de no tener que compilar ni publicar una versión totalmente nueva de sus apps. A su vez, si pueden tomar medidas rápidamente, la mayoría de sus usuarios jamás experimentarán el problema. Esto funciona bien con las alertas de correo electrónico que reciben de Firebase Crash Reporting cuando se observa un nuevo bloqueo.

Otra función de la configuración remota es la capacidad de experimentar con determinados aspectos de las apps, a fin de determinar lo más conveniente para los usuarios, y luego medir los resultados en Firebase Analytics. No sé si a ustedes les sucede lo mismo, pero en general tengo bastantes dificultades para intuir lo que desean las personas y a veces me sorprende el *uso* que podrían darle a una app. No hagan conjeturas; en lugar de ello, realicen un experimento y determinen con precisión lo que más satisfacción produce a sus usuarios. Es lógico que las apps configuradas de esta manera pueden obtener mejores calificaciones y generar más ganancias.

Base de datos en tiempo real de Firebase

Tiene sentido pensar que cuando se logra que resulte más sencillo para un usuario realizar tareas en sus apps, estos disfrutan más al usarlas y vuelven a hacerlo con mayor frecuencia. Algo que nunca me agradó es tener que buscar información nueva mediante actualizaciones o navegación hacia atrás y nuevamente hacia adelante. Resulta más placentera la experiencia de uso de las apps que siempre permanecen actualizadas sin necesidad de intervenciones.

Pueden lograr esto con sus apps usando de manera eficaz la base de datos en tiempo real de Firebase para brindar datos importantes de manera directa a sus usuarios en el momento en que se modifiquen en la base de datos. La base de datos en tiempo real es reactiva por naturaleza, ya que la API de cliente está diseñada para la configuración de receptores en ubicaciones de datos que se activan ante un cambio. Esto es mucho más conveniente que realizar repetidamente consultas a un terminal de API en busca de cambios, y también contribuye mucho más a preservar los datos móviles y la duración de la batería del dispositivo de un usuario. Los usuarios asocian esta sensación de satisfacción a las apps de alta calidad.

¿Qué deparará 2017 para sus apps?

Espero que se unan a mí este año para ampliar el esfuerzo por lograr que nuestros usuarios estén aun más satisfechos. Si comparten mi idea, no duden en enviarme tuits a @CodingDoug y comunicarme sus planes para 2017.



Supongamos que tienen una app de chat en la que desean habilitar una conversación entre personas de un grupo selecto o una app de intercambio de fotos a través de la cual buscan que un grupo de amigos cree un álbum en conjunto. ¿Cómo se puede limitar este uso compartido de datos a un grupo pequeño de usuarios y evitar exponerlo al público en general?


Supongamos que tienen una app de chat en la que desean habilitar una conversación entre personas de un grupo selecto o una app de intercambio de fotos a través de la cual buscan que un grupo de amigos cree un álbum en conjunto. ¿Cómo se puede limitar este uso compartido de datos a un grupo pequeño de usuarios y evitar exponerlo al público en general?

En este punto, las reglas de seguridad de Firebase pueden servirles. Las reglas de seguridad de Firebase pueden tener mucho alcance, pero a veces se requiere un poco de orientación para usarlas. Esto no se debe realmente a que sean complicadas, sino a que la mayoría de las personas suelen usarlas con suficiente frecuencia como para adquirir mucha experiencia.

Por fortuna, para todos ustedes, trabajo junto a personas que han acumulado experiencia en seguridad de Firebase y las he fastidiado durante varias semanas últimamente para que se redactara esta publicación. Lo que es más importante, descubrí un truco único y extraño que permite descubrir de manera sencilla reglas que compartiré con ustedes... al final de este artículo.

Por el momento, regresemos al ejemplo hipotético de una app de chat pensada para conversaciones grupales privadas. Quienes forman parte del grupo de chat pueden leer y escribir mensajes de este, pero no deseamos que otras personas participen.

Imagina que hemos estructurado nuestra base de datos para tal fin. Existen muchas formas de hacerlo, por supuesto, pero esta probablemente sea la más sencilla para una demostración.

Dentro de cada chat semiprivado, se encuentran una lista de personas que tienen permitido participar del chat y la lista de mensajes de chat. (En la realidad, estos userID tendrán un aspecto mucho más desorganizado que el de user_abc).

Por lo tanto, la primera regla de seguridad que nos convendrá configurar es la que establece que solo las personas de la lista de miembros puedan ver mensajes de chat. Podríamos crear algo así usando un conjunto de reglas de seguridad como la siguiente:
{
    "rules": {
      "chats": {
        "$chatID": {
          "messages": {
            ".read": "data.parent().child('members').child(auth.uid).exists()"
          }
        }
      }
    }
}

Con esto se otorga al usuario permiso para leer los chats de chats//messages mientras su userID exista en la sección members de ese mismo chat.

¿Les despierta curiosidad la línea $chatID? Es casi equivalente a un comodín totalmente adaptable, pero introduce la coincidencia a una variable $chatID a la que pueden hacer referencia posteriormente si lo desean.

¿Y user_abc? Supone un permiso absoluto para leer mensajes de chat. Sin embargo, user_xyz supone la denegación de tal permiso porque no hay entradas members/user_xyz dentro de ese grupo de chat.

Una vez hecho eso, será irrelevante agregar una regla similar que establezca que solo los miembros podrán escribir mensajes de chat.
"chats": {
  "$chatID": {
    "messages": {
      ".read": "data.parent().child('members').child(auth.uid).exists()",
      ".write": "data.parent().child('members').child(auth.uid).exists()"
    }
  }
}

Y es posible alcanzar un mayor nivel de detalle si se desea. ¿Y si nuestra app de chat tuviera un tipo de usuario "lurker" con permiso para ver mensajes y sin permiso para escribirlos?

Para esto también hay respuestas. Nos convendrá modificar nuestras reglas para decir “Puedes escribir mensajes, aunque solo si apareces en la lista de propietarios o miembros del chat”. El resultado se asemejaría a lo siguiente:
"chats": {
  "$chatID": {
    "messages": {
      ".read": "data.parent().child('members').child(auth.uid).exists()",
      ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'"
    }
  }
}

(Tengan en cuenta que retiré la línea de “reglas” del resto de estos ejemplos para ser más breve).

A propósito, probablemente pienses “Cielos, ¿no sería más fácil simplemente permitir que las personas escriban mensajes de chat solo si no aparecen como ‘lurker’”? Esto, por supuesto, restaría una línea de código.
"chats": {
  "$chatID": {
    "messages": {
      ".read": "data.parent().child('members').child(auth.uid).exists()",
      ".write": "data.parent().child('members').child(auth.uid).val() != 'lurker'"
    }
  }

Con frecuencia, sin embargo, cuando se trata de la seguridad los resultados son mejores cuando esta se basa en listas blancas en lugar de listas negras. Considera lo siguiente: ¿qué sucedería si en su app de pronto se agregara una nueva clase de usuarios (los llamaremos “principiantes”) y olvidaras actualizar esas reglas?

Con el primer conjunto de reglas, los usuarios de ese grupo nuevo no podrían realizar publicaciones, pero con el segundo podrían publicar lo que deseen. Los dos podrían resultarles inconvenientes si no ofrecen los resultados que buscan, pero el último podría ser mucho más perjudicial en términos de seguridad.

En todo esto, claro, se pasa por alto un pequeño problema: ¿cómo fue posible completar esas listas de usuarios en primer lugar?

Supongamos, por un momento, que un usuario puede de alguna manera obtener una lista de sus amigos a través de la app. (Dejaremos esto como un ejercicio para el lector). Existen algunas opciones que se pueden tener en cuenta para agregar usuarios nuevos a un chat grupal.
  1. Todos aquellos que ya formen parte del chat pueden agregar otras personas a este.
  2. Solo el propietario de un chat puede agregar otras personas a este.
  3. Cualquiera puede solicitar unirse al chat, pero el propietario debe dar su aprobación.

A decir verdad, cualquiera de estas opciones funcionará. Realmente será el desarrollador de la app quien determine la mejor experiencia de usuario para su app.

Veamos las opciones en orden.

Todos aquellos que ya formen parte del chat pueden agregar otras personas a este.


Para administrar esa primera opción, necesitaremos configurar una regla que indique “Quienes ya aparezcan en la lista de miembros tienen permiso para escribir mensajes a los miembros de esta”.

Esto se asemeja mucho a las reglas que ya configuramos para la publicación en la lista de miembros:
"chats": {
  "$chatID": {
    "messages": {
      ".read": "data.parent().child('members').child(auth.uid).exists()",
      ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'"
    },
    "members": {
      ".read": "data.child(auth.uid).exists()",
      ".write": "data.child(auth.uid).exists()"
    }
  }
}

Básicamente, se afirma que cualquier usuario tiene permiso para leer mensajes de miembros de la lista o escribir mensajes a ellos mientras su ID actual ya exista en la lista.

Solo el propietario de un chat puede agregar otras personas a este


Aplicar la limitación para que un propietario tenga permiso para escribir mensajes a miembros de la lista también es sencillo.
"chats": {
  "$chatID": {
    "messages": {
      ".read": "data.parent().child('members').child(auth.uid).exists()",
      ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'"
    },
    "members": {
      ".read": "data.child(auth.uid).val() == 'owner'",
      ".write": "data.child(auth.uid).val() == 'owner'"
    }
  }
}

Lo que se plantea es “Puedes escribirles a los miembros de una rama de un chat, pero solo si tu userID ya se encuentra allí y apareces como propietario”.

Por lo tanto, ese segundo caso queda abarcado.

Cualquiera puede solicitar unirse al chat, pero el propietario debe dar su aprobación


¿Qué sucede con la idea de permitir que los usuarios soliciten su incorporación y luego hacer que el propietario del chat los apruebe? Para esto, una buena opción sería agregar una lista pendiente a la base de datos junto a la lista de members, en la cual las personas pudieran agregarse a sí mismas.

El propietario del grupo tendría, entonces, permiso para agregar estos usuarios potenciales a la lista de miembros y borrarlos de la lista pendiente.

La primera regla que convendrá declarar es “Puedes agregar una entrada a la lista pendiente, aunque solo si te agregas tú mismo”. Dicho en otros términos, el aspecto fundamental del elemento que se agregará debe ser el ID de usuario propio.

La regla para agregar esto tiene el siguiente aspecto:
"chats": {
  "$chatID": {
    "messages": {
      ".read": "data.parent().child('members').child(auth.uid).exists()",
      ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'"
    },
    "members": {
      ".read": "data.child(auth.uid).val() == 'owner'",
      ".write": "data.child(auth.uid).val() == 'owner'"
    },
    "pending": {
      "$uid": {
        ".write": "$uid === auth.uid"
      }
    }
  }
}

Aquí, el mensaje es “Escribe lo que desees en la rama pending/ mientras el ID de usuario sea tu propio userID”.

Para aumentar el nivel de detalle, también se puede especificar que el usuario podrá hacer esto si no se agregó a sí mismo a la lista “pendiente”, que tendrá un aspecto más parecido al siguiente:
"pending": {
  "$uid": {
    ".write": "$uid === auth.uid && !data.exists()"
  }
}

Aprovechando la situación, también podemos especificar que una persona no puede agregarse a sí misma si ya forma parte del chat. No tendría sentido. Como resultado, se obtendrían reglas como la siguiente:
"pending": {
  "$uid": {
    ".write": "$uid === auth.uid && !data.exists() && !data.parent().parent().child('members').child($uid).exists()"
  }
}

Luego, podemos agregar algunas reglas a la carpeta pending con las cuales se determine que un propietario tiene permisos para leer mensajes de ella o enviarlos allí.
"pending": {
  ".read": "data.parent().child('members').child(auth.uid).val() === 'owner'",
  ".write": "data.parent().child('members').child(auth.uid).val() === 'owner'",
  "$uid": {
    ".write": "$uid === auth.uid && !data.exists() && !data.parent().parent().child('members').child($uid).exists()"
  }
}

¡Eso es todo! Suponiendo que conservamos las reglas de la sección anterior, que solo otorgan al propietario de un chat permisos para leer mensajes de miembros de una lista o escribir mensajes a ellos, hemos agregado con éxito las reglas de seguridad que permiten a un usuario quitar una entrada de la lista pendiente y luego agregarla a la lista de miembros.

Supongo que nos olvidamos una última regla que es bastante importante: permitir que alguien cree un nuevo chat grupal. ¿Cómo se puede configurar esto? Si se piensa en ello, es posible agregar la regla afirmando “Cualquiera tiene permiso para escribir mensajes a miembros de una lista si esta está vacía y te designas a ti mismo como propietario”.
"members": {
  ".read": "data.child(auth.uid).val() == 'owner'",
  ".write": "data.child(auth.uid).val() == 'owner' ||(!data.exists()&&newData.child(auth.uid).val()=='owner')"
}

A los efectos de este proceso de escritura, imaginen que comienzan realizando una operación de escritura en /chats/chat_345/members con un objeto { "user_zzz" : "owner" }. Esa nueva línea newData controlará este objeto y confirmará que el campo secundario con la clave del usuario con sesión activa (user_zzz) aparezca en la lista como propietario.

Una vez que hagas esto, podrás agregar los mensajes o usuarios adicionales que el propietario desee. Debido a que ahora oficialmente se mencionarán como propietarios, las reglas se seguridad deben permitir esas acciones sin problemas.

Tengan en cuenta que las reglas de seguridad realmente no incorporan un concepto de acción de “creación de directorios” separada. Si un usuario puede escribir mensajes a chat_456/messages/abc, la regla se aplica independientemente de que los mensajes ya existan. (Para el caso, chat_456).

¿Cómo hice para descubrir todo esto?


No soy experto en seguridad de Firebase, pero tengo la posibilidad de actuar como tal en las publicaciones del blog; sobre todo, al ejecutar el simulador de reglas.

Cada vez que se aplica un cambio a las reglas, y antes de su publicación, se puede probar su funcionamiento simulando operaciones de lectura o escritura en la base de datos. En la sección de reglas de la consola de Firebase, hay un botón “Simulator” que se puede accionar en la parte superior derecha. Este hará aparecer un formulario que les permitirá probar cualquier clase de acciones de lectura o escritura que deseen.

En este ejemplo, probaré esa última regla haciendo que un usuario con sesión activa como "user_zzz" intente agregarse a sí mismo como propietario en una lista /chats/chat_987/members vacía. El simulador de reglas me indica que esto está permitido y destaca la línea en la cual la acción de escritura se evalúa como true.

Técnicamente, se destaca la línea incorrecta. Se evalúa como true la parte de la regla en el paso 13. Creo que el marcador de resaltado no maneja bien los saltos de línea en particular.

Por otra parte, si ese usuario intenta agregarse a sí mismo como propietario de una lista que no está vacía, se producirá un error. Esto es exactamente lo que se busca.

Optimizaciones adicionales


Tengan en cuenta que pueden aplicarse otras optimizaciones a esto. En este momento, hemos aplicado una configuración que permite a los propietarios agregar miembros designados como propietarios. Esto puede, o no, ser lo que deseamos.

Si pensamos en ello, no hemos tomado medidas que validen la adición de miembros nuevos con roles legítimos. También existen, por supuesto, reglas de validación que se pueden agregar a los mensajes de chat para asegurarnos de que nuestra IU pueda controlar su extensión. Sin embargo, tal vez puedan experimentar en esta área.

Copien estas reglas finales y péguenlas en sus propias versiones de una app de chat, y vean lo que pueden hacer para agregar estas optimizaciones.
{
  "rules": {
    "chats": {
      "$chatID": {
        "messages": {
          ".read": "data.parent().child('members').child(auth.uid).exists()",
          ".write": "data.parent().child('members').child(auth.uid).val() == 'owner' || data.parent().child('members').child(auth.uid).val()=='chatter'"
        },
        "members": {
          ".read": "data.child(auth.uid).val() == 'owner'",
          ".write": "data.child(auth.uid).val() == 'owner' ||(!data.exists()&&newData.child(auth.uid).val()=='owner')"
        },
        "pending": {
          ".read": "data.parent().child('members').child(auth.uid).val() === 'owner'",
          ".write": "data.parent().child('members').child(auth.uid).val() === 'owner'",
          "$uid": {
            ".write": "$uid === auth.uid && !data.exists() && !data.parent().parent().child('members').child($uid).exists()"
          }
        }
      }
    }
  }
}

Los invito con gusto a consultar la documentación si necesitan más ayuda, y a hacer pruebas con el simulador. Les garantizo que usar un simulador de reglas de seguridad de bases de datos será la experiencia más entretenida que tendrán esta semana o que al menos estará entre las tres mejores.



Esta es nuestra tercera publicación de la serie de métricas piratas de Firebase. En la primera publicación, te acercamos información general sobre las métricas piratas y las razones por las cuales son importantes.


Esta es nuestra tercera publicación de la serie de métricas piratas de Firebase. En la primera publicación, te acercamos información general sobre las métricas piratas y las razones por las cuales son importantes. En la segunda, te mostramos la manera en que puedes usar Firebase para mejorar tu estrategia de adquisición.

Una vez que adquieras un usuario, tu objetivo principal será asegurarte de que use tu producto. Los usuarios a menudo instalan apps, pero nunca se entusiasman. Si tienes suerte, conservarán la tuya un día o dos antes de olvidarse de ella o, lo que es aun peor, desinstalarla. Se desperdiciará todo el esfuerzo que dediques a tu proceso de adquisición.





Por lo tanto, los primeros días serán cruciales. A través de tus datos, buscarás un patrón para determinar el punto en el cual un usuario se incentiva y también las maneras de lograr que más usuarios avancen más allá de ese punto. Entre los factores que ejemplifican esto pueden contarse el número de amigos en aplicaciones de redes sociales o la cantidad de niveles superados en un videojuego. Para diseñar la “estrategia de acicate” correcta siempre se necesita muchísima experimentación,

y tenemos la herramienta adecuada para que realices estos experimentos: la configuración remota de Firebase. Esta herramienta te permite configurar pares clave-valor en el servidor y usarlos para diversificar la experiencia en tu aplicación. Cuando se actualizan en la consola, estos valores se reflejan en tu propia aplicación. Esto te permite cambiar la experiencia para los usuarios sin lanzar una actualización.

Si usas esta capacidad de configuración remota, y fijas valores usando el perfilamiento de “percentil aleatorio”, básicamente dispondrás de una configuración de pruebas A/B. Luego podrás ver el impacto en tu análisis y cambiar estos valores de manera dinámica en el propio servidor, con lo cual aumentará la implementación de experimentos de funcionamiento comprobado. Esto representa una gran solución en términos de pruebas A/B.

Para optimizar tus pruebas, te recomendamos primero definir los puntos de datos que desees mejorar (como un aumento en el número de usuarios que se registran al abrir por primera vez una app). Luego, imagina los experimentos que desearás ejecutar para mejorar estos puntos de datos. Podrían ser experimentos en los cuales se realice un seguimiento del impacto de diferentes tutoriales o métodos de registro para apps, o de configuraciones de dificultad de niveles iniciales en un juego que en última instancia puedan mejorar tu porcentaje de acicate.

Bloguera invitada Jen Looper


La poderosa combinación de NativeScript, Firebase y Angular 2 tiene la capacidad de potenciar la creación de apps. Esto puede ocurrir en especial durante los días festivos, cuando se enfrentan a la necesidad de acelerar el desarrollo de sus apps, ADEMÁS DE satisfacer las necesidades de sus familias respecto de la entrega de regalos. En el momento indicado, tengo la satisfacción de ...
Bloguera invitada Jen Looper


La poderosa combinación de NativeScript, Firebase y Angular 2 tiene la capacidad de potenciar la creación de apps. Esto puede ocurrir en especial durante los días festivos, cuando se enfrentan a la necesidad de acelerar el desarrollo de sus apps, ADEMÁS DE satisfacer las necesidades de sus familias respecto de la entrega de regalos. En el momento indicado, tengo la satisfacción de presentarles (observen lo que hice 🎁) una demostración sobre la manera de aprovechar Firebase en sus apps de NativeScript con tecnología de Angular 2 usando varios elementos del famoso complemento de Firebase para NativeScript, de Eddy Verbruggen.

En este instructivo, les mostraré la manera de usar cuatro elementos populares de Firebase en sus apps de NativeScript: la autenticación, con una rutina de acceso y registro; la base de datos, para el almacenamiento de datos y la actualización en tiempo real; la configuración remota, para realizar cambios de manera remota en una app; y el almacenamiento, para guardar fotos. Para hacer esto, decidí reescribir mi app Giftler, originalmente escrita en Ionic.

A modo de preparación, los invito a leer la documentación antes de comenzar con su proyecto para asegurarse de que se cumplan algunos requisitos previos:
  • Asegúrense de que NativeScript esté instalado en sus equipos locales y de que CLI funcione como se espera.
  • Configuren su IDE preferido para el desarrollo con NativeScript y Angular. Necesitarán TypeScript. Por lo tanto, asegúrense de que sus procesos de transpilación funcionen. Hay disponibles excelentes complementos de NativeScript para IDE compatibles con Visual Studio, Visual Studio Code y Jetbrains, entre otros. En particular, Visual Studio Code tiene fragmentos prácticos que agilizan el desarrollo.
  • Accedan a sus cuentas de Firebase y encuentren sus consolas.
  • Creen un nuevo proyecto en la consola de Firebase. Le puse el nombre “Giftler” al mío. Creen también una app de iOS y Android en la consola de Firebase. Como parte de este proceso, descargarán un archivo GoogleServices-Info.plist y un archivo google-services.json. Asegúrense de tomar nota de la ubicación que asignen a esos archivos, ya que los necesitarán pronto.


Instalen las dependencias


Creé Giftler como ejemplo de una app de NativeScript en la cual los usuarios pueden hacer una lista de los regalos que desearían recibir durante las fiestas, con la posibilidad de incluir fotos y descripciones de texto. Por el momento, esta app hace lo siguiente en iOS y Android:
  • Permite el acceso, el cierre de sesión, el registro y una rutina de “olvido de contraseña”.
  • Permite a los usuarios ingresar artículos de regalo en una lista.
  • Permite a los usuarios borrar artículos de regalo de una lista.
  • Permite a los usuarios editar artículos de la lista de manera individual agregando descripciones y fotos.
  • Proporciona mensajería del servicio de configuración remota de Firebase que puede modificarse rápidamente en el backend.

Ahora, bifurquen el código fuente de Giftler, que es una app completa y funcional. Una vez que sus apps estén clonadas, reemplacen los archivos orientados a Firebase actuales que descargaron al crearlas:
  • En la carpeta /app/App_Resources/Android, dispongan el archivo google.services.json que descargaron de Firebase.
  • De manera similar, en la carpeta /app/App_Resources/iOS dispongan el archivo GoogleService-Info.plist también descargado de Firebase.

Estos archivos se necesitan para inicializar Firebase en sus apps y conectarlas a los servicios externos correspondientes.

A continuación, veamos el archivo package.json y la raíz de esta app. Contiene los complementos que usarán en esta app. Presten atención a los complementos orientados a NativeScript:
"nativescript-angular": "1.2.0",
"nativescript-camera": "^0.0.8",
"nativescript-iqkeyboardmanager": "^1.0.1",
"nativescript-plugin-firebase": "^3.8.4",
"nativescript-theme-core": "^1.0.2",

El complemento “nativeScript-angular” representa la integración de Angular por parte de NativeScript. El complemento “camera” facilita un poco la administración de la cámara. “Iqkeyboardmanager” es un complemento específico de iOS que controla el teclado complejo en iOS. El complemento “theme” es una excelente opción para agregar estilos predeterminados a sus apps sin necesidad de cambiar su aspecto visual por completo. Por último, el complemento más importante de esta app es “Firebase”.

Una vez que las dependencias se encuentren en su lugar y los complementos estén listos para la instalación, podrán compilar sus apps para crear sus carpetas platforms con código específico de iOS y Android e inicializar el complemento de Firebase junto con el resto de los complementos basados en npm. Usando NativeScript CLI, diríjanse a la raíz de sus apps clonadas y escriban tns run ios o tns run android. Con esto, se iniciarán las rutinas de compilación de complementos y, en particular, verán el inicio de la instalación de las diferentes partes del complemento de Firebase. La secuencia de comandos que se ejecute les solicitará instalar varios elementos para integrarlos a los diferentes servicios de Firebase. Seleccionaremos todo, a excepción de la mensajería y la autenticación social por el momento. Una excelente característica consiste en que el archivo firebase.nativescript.json se instala en la raíz de la app. Por ello, si necesitan instalar una nueva parte del complemento posteriormente, pueden editar el archivo en cuestión y reinstalar el complemento.

En este punto, si ejecutan tns livesync ios --watch o tns livesync android --watch para ver la app en ejecución en un emulador y buscar cambios, verán que esta funciona y está lista para aceptar sus nuevas credenciales. Sin embargo, antes de inicializar un acceso asegúrense de que Firebase controle accesos de correo electrónico y contraseña habilitando esta función en la consola de Firebase, en la pestaña Authentication:

Veamos lo que sucede tras bambalinas. Para acceder a Firebase, deben inicializar los servicios de Firebase que instalaron. En app/main.ts, hay algunos elementos interesantes.
// this import should be first in order to load some required settings (like
globals and reflect-metadata)
import { platformNativeScriptDynamic } from "nativescript-angular/platform";



import { AppModule } from "./app.module";
import { BackendService } from "./services/backend.service";





import firebase = require("nativescript-plugin-firebase");





firebase.init({
  //persist should be set to false as otherwise numbers aren't returned during
livesync
  persist: false,
  storageBucket: 'gs://giftler-f48c4.appspot.com',
  onAuthStateChanged: (data: any) => {
    console.log(JSON.stringify(data))
    if (data.loggedIn) {
      BackendService.token = data.user.uid;
    }
    else {
      BackendService.token = "";
    }
  }
}).then(
  function (instance) {
    console.log("firebase.init done");
  },
  function (error) {
    console.log("firebase.init error: " + error);
  }
  );
platformNativeScriptDynamic().bootstrapModule(AppModule);



Primero se importa firebase del complemento y luego se llama a .init(). Editen la propiedad storageBucket para que refleje el valor de la pestaña Storage de sus consolas de Firebase:

Ahora sus apps estarán personalizadas según sus cuentas de Firebase y podrán registrar un nuevo usuario y método de acceso en ellas. Pueden editar las variables user.email y password del archivo app/login/login.component.ts para cambiar las credenciales de acceso predeterminadas de user@nativescript.org a sus nombres de usuario y contraseñas si lo desean.


Pantallas de acceso de iOS y Android
Nota: usando el simulador de Xcode, tendrán la posibilidad de emular sus apps de inmediato en iOS. En Android, sin embargo, es posible que deban realizar algunos pasos para la instalación de la app en el emulador; por ejemplo, habilitar los servicios de Google. A continuación, se ofrece un instructivo relacionado con la manera de hacerlo en Genymotion, mi emulador de Android favorito.

Estructura y autenticación de código


Los patrones de diseño de Angular 2 exigen dividir el código en módulos. Por ello, cumpliremos con este requisito usando la siguiente estructura de código:

—login
  1. login.component.ts
  2. login.html
  3. login.module.ts
  4. login.routes.ts

—list …

—list-detail …

—models
  1. gift.model.ts
  2. user.model.ts
  3. index.ts

—services
  1. backend.service.ts
  2. firebase.service.ts
  3. utils.service.ts
  4. index.ts

app.component.ts

app.css

app.module.ts

app.routes.ts

auth-guard.service.ts

main.ts

Presten atención a la forma en que la autenticación de Firebase funciona con auth-guard.service de Angular 2. Cuando Firebase se inicializa en sus apps en app/main.ts, como vimos antes, se llama a la función onAuthStateChanged:
onAuthStateChanged: (data: any) => {
    console.log(JSON.stringify(data))
    if (data.loggedIn) {
      BackendService.token = data.user.uid;
    }
    else {
      BackendService.token = "";
    }
  }

Cuando la app se inicie, busquen en la consola los datos convertidos en strings que muestre Firebase. Si este usuario se marca como loggedIn, simplemente configuraremos un token que represente el userId enviado por Firebase. Usaremos el módulo de configuración de NativeScript, que funciona como localStorage, para hacer que este userId siga estando disponible y asociarlo a los datos que creemos. Este token y las pruebas de autenticación que lo usan, administradas en el archivo app/services/backend.service.ts, quedan disponibles para el archivo app/auth-guard.service.ts. El archivo auth-guard ofrece una buena manera de administrar el estado de acceso y cierre de sesión de la app.

La clase AuthGuard implementa la interfaz CanActivate del módulo Router de Angular.
export class AuthGuard implements CanActivate {
  constructor(private router: Router) { }



  canActivate() {
    if (BackendService.isLoggedIn()) {
      return true;
    }
    else {
      this.router.navigate(["/login"]);
      return false;
    }
  }
Básicamente, si el token se configura durante la rutina de acceso anterior, y la función BackendService.isLoggedIn muestra true, la app tiene autorización para navegar hasta la ruta predeterminada que representa nuestra lista de deseos. De lo contrario, el usuario regresa al acceso:
const listRoutes: Routes = [
  { path: "", component: ListComponent, canActivate: [AuthGuard] },
];

Ahora que inicializaron sus apps de NativeScript con tecnología de Firebase, veamos la forma de cargarles datos y de usar el increíble poder en tiempo real de Firebase para buscar la base de datos que se actualizará.

Creación de una lista y doble verificación de esta


A partir de app/list/list.html, que es la base de la lista de deseos, verán un campo de texto y una lista vacía. ¡Adelante, indíquenle a Santa lo que desean! Los artículos se envían a la base de datos y se agregan a sus listas en tiempo real. Veamos la manera de hacer esto.

Primero, observen que en app/list/list.component.ts se configura un elemento observable que contendrá la lista de regalos:
public gifts$: Observable;
Luego, se completa la lista desde la base de datos cuando se inicializa el componente:
ngOnInit(){
  this.gifts$ = this.firebaseService.getMyWishList();
}

Lo interesante sucede en el archivo firebaseService. Observen la forma en que esta función agrega un receptor y muestra un elemento observable rxjs al buscar cambios en la colección Gifts de la base de datos de Firebase:
getMyWishList(): Observable {
  return new Observable((observer: any) => {
      let path = 'Gifts';
        let onValueEvent = (snapshot: any) => {
          this.ngZone.run(() => {
            let results = this.handleSnapshot(snapshot.value);
            console.log(JSON.stringify(results))
             observer.next(results);
          });
        };
        firebase.addValueEventListener(onValueEvent, `/${path}`);
    }).share();
}

Los resultados de esta consulta se controlan en una función handleSnapshot como la siguiente, que filtra los datos por usuario y completa una matriz _allItems:
handleSnapshot(data: any) {
    //empty array, then refill and filter
    this._allItems = [];
    if (data) {
      for (let id in data) {
        let result = (Object).assign({id: id}, data[id]);
        if(BackendService.token === result.UID){
          this._allItems.push(result);
        }
      }
      this.publishUpdates();
    }
    return this._allItems;
  }

Por último, se llama a publishUpdates, que ordena los datos por fecha para que se muestren primero los artículos más nuevos:
publishUpdates() {
    // here, we sort must emit a *new* value (immutability!)
    this._allItems.sort(function(a, b){
        if(a.date < b.date) return -1;
        if(a.date > b.date) return 1;
      return 0;
    })
    this.items.next([...this._allItems]);
  }

Una vez que el elemento observable $gifts se complete con datos, podrán editar y borrar elementos de él, y lo controlarán el receptor y el front end correctamente actualizados. Tengan en cuenta que la función onValueEvent del método getMyWishList incluye el uso de la clase ngZone; esta garantiza que la IU se actualice como corresponde aunque los datos lo hagan de manera asincrónica. Aquí se puede hallar buena información general sobre ngZone en apps de NativeScript.

Mensajes de origen lejano con configuración remota

En otra área atractiva del servicio de Firebase se incluye la “configuración remota”, una vía para proporcionar actualizaciones de apps desde el backend de Firebase. Pueden usar la configuración remota para activar o desactivar funciones en sus apps, realizar cambios en la IU o enviar mensajes de Santa, que es lo que vamos a hacer.


En app/list/list.html, encontrarán un cuadro de mensaje
<Label class="gold card" textWrap="true" [text]="message$ | async"></Label>
. La compilación del elemento observable message$ es muy similar a la de la lista de datos; los cambios se recopilan en este caso cada vez que la app se inicializa de nuevo:
ngOnInit(){
  this.message$ = this.firebaseService.getMyMessage();
}
Y la magia tiene lugar en la capa de servicio (app/services/firebase.service.ts ):
getMyMessage(): Observable{
    return new Observable((observer:any) => {
      firebase.getRemoteConfig({
      developerMode: false,
      cacheExpirationSeconds: 300,
      properties: [{
      key: "message",
      default: "Happy Holidays!"
    }]
  }).then(
        function (result) {
          console.log("Fetched at " + result.lastFetch + (result.throttled ? "
(throttled)" : ""));
          for (let entry in result.properties)
            {
              observer.next(result.properties[entry]);
            }
        }
    );
  }).share();
}



Podrán publicar mensajes nuevos con la frecuencia que deseen.
Nota: la modificación reiterada de la configuración remota puede limitar sus instancias de Firebase. Por ello, deben realizar el desarrollo con cuidado.

Podrán tomar una foto.


Creo que uno de los aspectos más interesantes de este proyecto es la capacidad de tomar una foto del artículo deseado y guardarla en el almacenamiento de Firebase. Como lo mencioné antes, aproveché el complemento Camera; este facilita un poco la administración del hardware. En primer lugar, asegúrense de que sus apps tengan acceso a la cámara del dispositivo obteniendo permisos configurados en el método ngOnInit() de app/list-detail/list-detail.component.ts:
ngOnInit(){
   camera.requestPermissions();
   ...
  }

Una cadena de eventos se inicia cuando el usuario hace clic en el botón “Photo”, en la pantalla de detalles. Primero:
takePhoto() {
  let options = {
            width: 300"
            height: 300"
            keepAspectRatio: true,
            saveToGallery: true
        };
    camera.takePicture(options)
        .then(imageAsset => {
            imageSource.fromAsset(imageAsset).then(res => {
                this.image = res;
                //save the source image to a file, then send that file path to
firebase
                this.saveToFile(this.image);
            })
        }).catch(function (err) {
            console.log("Error -> " + err.message);
        });
}

La cámara toma una foto que luego se almacena como imageAsset y se muestra en pantalla. La imagen recibe un nombre y una fecha, y se guarda en un archivo local. Esa ruta de acceso se reserva para usos futuros.
saveToFile(res){
  let imgsrc = res;
        this.imagePath =
this.utilsService.documentsPath(`photo-${Date.now()}.png`);
        imgsrc.saveToFile(this.imagePath, enums.ImageFormat.png);
}

Cuando se presiona el botón “Save”, esta imagen se envía a través de su ruta de acceso local a Firebase y se guarda en el módulo de almacenamiento. Su ruta de acceso completa en Firebase se devuelve a la app y se almacena en la colección de la base de datos de /Gifts:
editGift(id: string){
  if(this.image){
    //upload the file, then save all
    this.firebaseService.uploadFile(this.imagePath).then((uploadedFile: any) =>
{
          this.uploadedImageName = uploadedFile.name;
          //get downloadURL and store it as a full path;
this.firebaseService.getDownloadUrl(this.uploadedImageName).then((downloadUrl:
string) => {
this.firebaseService.editGift(id,this.description,downloadUrl).then((result:any)
=> {
              alert(result)
            }, (error: any) => {
                alert(error);
            });
          })
        }, (error: any) => {
          alert('File upload error: ' + error);
        });
  }
  else {
    //just edit the description
    this.firebaseService.editDescription(id,this.description).then((result:any)
=> {
        alert(result)
    }, (error: any) => {
        alert(error);
    });
  }
}

Esta cadena de eventos parece complicada, pero se resume a unas pocas líneas en el archivo de servicio de Firebase:
uploadFile(localPath: string, file?: any): Promise {
      let filename = this.utils.getFilename(localPath);
      let remotePath = `${filename}`;
      return firebase.uploadFile({
        remoteFullPath: remotePath,
        localFullPath: localPath,
        onProgress: function(status) {
            console.log("Uploaded fraction: " + status.fractionCompleted);
            console.log("Percentage complete: " + status.percentageCompleted);
        }
      });
  }
  getDownloadUrl(remoteFilePath: string): Promise {
      return firebase.getDownloadUrl({
     remoteFullPath: remoteFilePath})
      .then(
        function (url:string) {
          return url;
        },
        function (errorMessage:any) {
          console.log(errorMessage);
        });
}
editGift(id:string, description: string, imagepath: string){
    this.publishUpdates();
    return firebase.update("/Gifts/"+id+"",{
        description: description,
        imagepath: imagepath})
      .then(
        function (result:any) {
          return 'You have successfully edited this gift!';
        },
        function (errorMessage:any) {
          console.log(errorMessage);
        });
  }

El resultado final es una buena alternativa para capturar fotos y descripciones de los regalos de sus listas de deseos. Santa ya no tendrá la excusa de que no sabía CUÁL lápiz de ojos Kylie comprar. Combinando el poder de NativeScript y Angular, pueden crear una app nativa de iOS y Android en minutos. Al agregar Firebase, pueden acceder a una poderosa alternativa para almacenar los usuarios, las imágenes y los datos de sus apps, y a un método para actualizar estos datos en tiempo real en diferentes dispositivos. Genial, ¿no es así? El aspecto resultante será el siguiente:

Hemos establecido las bases para crear una app de administración de listas de deseos sólida. Nos queda determinar la mejor manera de comunicar a Santa nuestros deseos: realizar una integración de correo electrónico de Mailgun o usar notificaciones de aplicación sería el próximo paso obvio. Mientras tanto, les hago llegar el deseo de que tengan unas felices fiestas y espero que disfruten mucho creando fabulosas apps de NativeScript con Firebase.

¿Desean más información sobre NativeScript? Visiten http://www.nativescript.org. Si necesitan asistencia, súmense al canal de NativeScript en Slack aquí.

Publicado por Greg Zecchini, ingeniero de software de Google Classroom

Cientos de programadores de aplicaciones educativas usan la API Classroom para integrar sus aplicaciones con ...
 Publicado por Greg Zecchini, ingeniero de software de Google Classroom

Cientos de programadores de aplicaciones educativas usan la API Classroom para integrar sus aplicaciones con Google Classroom. El año pasado, anunciamos la posibilidad lectura y escritura de deberes a través de aplicaciones externas. Hemos recibido muchísimos comentarios de programadores que comenzaron a integrar deberes con Classroom, y nos dijeron que querían ampliar aún más su campo de acción.
Hoy, haremos que los extremos del trabajo de curso sean más sólidos e incorporen capacidades de administración adicionales. Ahora, las aplicaciones pueden hacer lo siguiente:
  • Crear y actualizar preguntas.
  • Modificar y borrar el trabajo de curso.
  • Agregar elementos de Drive y videos de YouTube al trabajo de curso y las entregas de los alumnos.
Flat Education, una app de notación musical colaborativa, ya se integra con Classroom y permite a los docentes sincronizar rápidamente listas de asistencia y deberes, y diseñar actividades musicales para estudiantes con un clic desde Google Classroom. En Flat, se ha aplicado la nueva funcionalidad de API para una integración más uniforme. Vincent Giersch, CTO de Flat, afirma que “la compatibilidad con Google Drive en la API Google Classroom es simplemente fabulosa: al ser archivos de Drive todas las composiciones musicales creadas en cuentas G Suite for Education en Flat, ahora podemos agregar directamente nuestros archivos de Drive en los deberes y las entregas de los estudiantes. Esto permitirá que los docentes y estudiantes usen Google Classroom con Flat como lo harían con Documentos de Google”.


En caso de que no se hayan enterado, también realizamos recientemente otras actualizaciones en la gestión de padres y tutores con la API. Classroom envía a los tutores resúmenes diarios o semanales y las aplicaciones que usan la API para la gestión de tutores ahora pueden determinar si se encuentra habilitada la visibilidad de estos para un curso, y listar la totalidad de tutores e invitaciones de un dominio.

Para obtener más información sobre lo que se puede crear con el trabajo del curso, consulten la documentación y las preguntas frecuentes, y publiquen preguntas en Stack Overflow. Como siempre, usen el hashtag #withClassroom en Twitter o Google+ para ponernos al corriente de sus creaciones. Esperamos trabajar juntos con el objetivo que sea aún más sencillo para los docentes y estudiantes usar con Classroom las herramientas que más les agradan.



Hoy presentamos estadísticas de ubicación de empresas en la API de Google Mi Negocio, para que los desarrolladores de aplicaciones de terceros y las empresas con varias ubicaciones puedan, de manera más sencilla, acceder mediante programación a información de ubicación, como el número total de búsquedas, vistas y acciones que permiten a los propietarios de empresas controlar y analizar los lugares y la forma en que la gente las encuentra en Google.


Hoy presentamos estadísticas de ubicación de empresas en la API de Google Mi Negocio, para que los desarrolladores de aplicaciones de terceros y las empresas con varias ubicaciones puedan, de manera más sencilla, acceder mediante programación a información de ubicación, como el número total de búsquedas, vistas y acciones que permiten a los propietarios de empresas controlar y analizar los lugares y la forma en que la gente las encuentra en Google.

Los desarrolladores ahora pueden usar la API de Google Mi Negocio a fin de solicitar datos de períodos de hasta 18 meses para cada una de las ubicaciones de empresas, y crear aplicaciones que agreguen y visualicen estas estadísticas de manera productiva. Por ejemplo, para una cafetería con cientos de ubicaciones diferentes ahora es posible comparar y comprender fácilmente tendencias de estas, como el número de vistas de usuarios, las solicitudes de clics para obtener indicaciones y las llamadas telefónicas, entre otros datos. Se pueden usar estas estadísticas para asignar de mejor manera recursos en las ubicaciones y controlar el efecto de las actividades de marketing en la empresa.

Esta nueva funcionalidad de API acerca las funciones de nuestro panel de Google Mi Negocio a tus propias herramientas de análisis de datos. Los usuarios de interfaces web pueden generar una tabla con los últimos 90 días de información de Google Mi Negocio:
Datos de ejemplo visibles a través del panel web de Google Mi Negocio.

Ahora se encuentran disponibles los datos subyacentes a través de la API. Dar los primeros pasos es sencillo gracias a nuestra nueva documentación para desarrolladores. A continuación, se ofrece una solicitud HTML que proporciona un desglose de la cantidad de búsquedas en un directorio de empresas en la Búsqueda de Google y Google Maps:

SOLICITUD:
POST https://mybusiness.googleapis.com/v3/123456/locations:reportInsights
{
  "locationNames": [
    “accounts/110714876951578713336/locations/14372810722634034850”,
  ],
  “basicRequest” : {
          "metricRequests": [
             {
               "metric": QUERIES_DIRECT,
             },
             {
               "metric": QUERIES_INDIRECT,
             }
          ],
          "timeRange": {
               "startTime": 2016-10-12T01:01:23.045123456Z,
               "endTime": 2017-01-10T23:59:59.045123456Z,
          },
    },
}

RESPUESTA:
{
  "locationMetrics": [
    {
      "locationName": "accounts/110714876951578713336/locations/
                       14372810722634034850",
      "timeZone": "America/Los_Angeles",
      "metricValues": [
        {
          "metric": "QUERIES_DIRECT",
          "totalValue": {
            "metricOption": "AGGREGATED_TOTAL",
            "timeDimension": {
              "timeRange": {
                "startTime": "2016-10-12T04:00:00Z",
                "endTime": "2017-01-10T04:00:00Z"
              }
            },
            "value": "36738"
          }
        },
        {
          "metric": "QUERIES_INDIRECT",
          "totalValue": {
            "metricOption": "AGGREGATED_TOTAL",
            "timeDimension": {
              "timeRange": {
                "startTime": "2016-10-12T04:00:00Z",
                "endTime": "2017-01-10T04:00:00Z"
              }
            },
            "value": "81770"
          }
        }
      ]
    }
  ]
}

A continuación, se ofrece un ejemplo en el que se capturan estadísticas en los puntos desde los cuales los clientes solicitan indicaciones de manejo para llegar a una empresa:

SOLICITUD:
POST https://mybusiness.googleapis.com/v3/123456/locations:reportInsights
{
     “locationNames": [
             “accounts/110714876951578713336/locations/14372810722634034850”,
       ],
      "drivingDirectionsRequest”: {
            "numDays": NINETY,
      },
}

RESPUESTA (truncada para mostrar los 3 primeros resultados):
{
  "locationDrivingDirectionMetrics": [
    {
      "locationName": "accounts/110714876951578713336/locations/
                       14372810722634034850",
      "topDirectionSources": [
        {
          "dayCount": 90,
          "regionCounts": [
            {
              "latlng": {
                "latitude": 37.789,
                "longitude": -121.392
              },
              "label": "94105",
              "count": "2980",
            },
            {
              "latlng": {
                "latitude": 37.779,
                "longitude": -122.421
              },
              "label": "94102",
              "count": "887",
            },
            {
              "latlng": {
                "latitude": 37.773,
                "longitude": -122.410
              },
              "label": "94103",
              "count": "886",
            }
          ]
        }
      ],
          "timeZone": "America/Los_Angeles"
    }
  ]
}
Datos de ejemplo visibles a través del panel web de Google Mi Negocio.

Con esta nueva función, los usuarios de la API de Google Mi Negocio pueden optimizar sus directorios para impulsar acciones de los clientes a través de la comprensión de estadísticas claves sobre la forma en que estos buscan empresas en Google, y las acciones que realizan una vez que las encuentran. Estas estadísticas también se encuentran disponibles en plataformas web y móviles de Google Mi Negocio, lo que permite a los usuarios realizar un seguimiento de tendencias claves desde cualquier ubicación.

Para obtener más información sobre la API de Google Mi Negocio y solicitar acceso, visita nuestra página para desarrolladores. ¿Tienes dudas o comentarios? Comunícate con el equipo de la API en el foro de la API de Google Mi Negocio.