Capacitor & Riot

Ionic Framework proporciona sus propios estilos y componentes incorporados para brindar la apariencia de aplicaciones que pueden correr tanto en mobiles como en el escritorio con una vision unificadora, y con la tecnologia Capacitor (del mismo fabricante de Ionic) puede acceder a caracteristicas de los dispositivos (como camara, notificaciones, acceso a archivos, GPS, etc) usando Javascript.

No obstante, Capacitor puede combinarse con otras librerias distintas de las disponibles por Ionic y sin la necesidad de dicho framework (incluso puede usar Javascript puro), por ejemplo podria utilizarse perfectamente con Riot. Este tipo de aplicaciones corresponde en prinicipio a desarrollo movil no nativo, conocido como hibrido (que utiliza tecnologia web mediante un componente interno denominado WebView para iOS y Android), aunque sobre la base de Capacitor se puede obtener un alcance nativo al incorporar el servicio de Appflow que es de pago.

Instalación de Capacitor

Teniendo un entorno Node.js ya instalado, nos ubicamos en una carpeta que se use como espacio de trabajo y ejecutamos los siguientes comandos:

npm install @capacitor/cli @capacitor/core
npx @capacitor/cli create
cd project
npx cap serve

npx @capacitor/cli create solicita el nombre de la app (o carpeta del proyecto, como ejemplo project) y crea la estructura sin librería de UI con un ejemplo. Para verificar se abre un navegador con la dirección: localhost:3333

Se puede agragar destino por ejemplo para Android, iOS o Electron (para PWA) respectivamente así:

npx cap add android
npx cap add ios
npx cap add electron

Las herramientas de desarrollo que proporcionan entornos con emulador son requeridas según el sistema de tu máquina, Xcode para iOS y Android Studio para Adnroid. En este documento abordaremos el caso de Android.

Teniendo el Android Studio instalado y configurado, puedes usar Capacitor ejecutando:

npx cap copy
npx cap open android

La primera línea copia los archivos de recursos web del proyecto (conocidos como assets) para el destino con Android, mientras la segunda línea abre Android Studio. Tambien puedes importar la carpeta android del proyecto para gestionarlo directamente con Android Studio.

Si deseas visualizar el proyecto de ejemplo en un navegador, ejecutas:

npx cap serve

Recordando la noción de una etiqueta HTML

Si pensamos en un botón, que se visualiza en una aplicacion o una página web, tendríamos una etiqueta como la siguiente:

<button style="color: blue" onsubmit="alert('Hello')">Press</button>

Se puede observar que:

  1. Generalmente se usa apertura y cierre con el nombre de la etiqueta entre signos menor y mayor que, el último con un “slash” (/), es decir: <button></button>
  2. La etiqueta puede reportar atributos (o propiedades), en este caso: style
  3. La etiqueta puede disparar eventos, en este caso: onsubmit
  4. La etiqueta puede tener contenido, bien sea texto o más HTML, en este caso el texto: Press

Comprendiendo esto se construye una plantilla con las etiquetas predefinidas para HTML. Este mismo principio se aplica a componentes web que pueden verse como etiquetas pesonalizadas y pueden tener atributos, eventos y contenido (o subcomponentes).

Riot para la UI con Capacitor

Si no conoces esta libreria para la UI puedes acceder al enlace de referencia. En el sentido esencial de las cosas, Riot se acerca a una plantilla HTML agregando en principio 3 aspectos:

  1. Expresiones entre llaves {}, las cuales son utilizadas para mostrar contenido de modo dinámico, por ejemplo un título de manera programada en una página bilingue.
  2. Condiciones if para evaluar si una etiqueta HTML se muestra o no conforme a una expresión.
  3. Ciclos each para procesar de modo dinámico datos usados frecuentemente en listas, tablas, opciones de selección o similar.

Ahora revisemos la estructura esencial del proyecto creado anteriormente con Capacitor.

Para incluir Riot se requiere:

Incorporando Riot

Consideramos ahora agregar la carpeta src y algunos archivos nuevos para incorporar lo que haremos con Riot. Veamos:

Para agregar e instalar Riot en el proyecto ejecutamos lo siguiente:

npm install riot @riotjs/webpack-loader @riotjs/compiler -D
npm install webpack webpack-cli -D

Webpack se requiera para traducir el código de un componente web o etiqueta de Riot a Javascript (compila el código). El archivo de configuración asociado generalmente se denomina webpack.config.js, lo inicializamos (creándolo en un editor como Visual Studio Code) con el siguiente contenido:

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'www', 'js'),
    filename: 'index.bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.riot$/,
        exclude: /node_modules/,
        use: [{
          loader: '@riotjs/webpack-loader',
          options: { hot: false }
        }]
      }
    ]
  }
}

Modificamos el archivo de configuración package.json para incorporar la tarea build (bajo scripts), así:

  "scripts": {
    "build": "webpack --mode development && npx cap copy",
  },

Esto configurará la tarea para el comando npm build. Podría omitirse esto y en su lugar usar directamente las sentencias cuando se requieran así:

npx webpack
npx cap copy

Bajo la carpeta www encontramos el archivo index.html. Modificamos el código del bloque o etiqueta body, la cual quedaría con el siguiente contenido:

<body>
  <div id="app"></div>

  <script src="capacitor.js"></script>
  <script src="js/index.bundle.js"></script>
</body>

Además, en la carpeta src abrimos el archivo del componente de Riot, por ejemplo my-app.riot, con el siguiente contenido:

<my-app>
    <div id="container" if={ (!state.captured) }>
        <h3>{ props.title }</h3>
        <button class="button" onclick={ takePhoto }>Take Photo</button>
    </div>
    <div style="text-align: center" if={ (state.captured) }>
        <img id="image" style="max-width: 100%" src={ state.imageUrl }><br/>
        <button class="button" onclick={ onAgain }>Again</button>
    <div>

    <style>
        #container {
            font-family: "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            text-align: center;
            position: absolute;
            left: 0;
            right: 0;
            top: 40%;
        }

        .button {
            padding: 10px;
            background-color: steelblue;
            color: #fff;
            font-size: 0.9em;
            border: 0;
            border-radius: 3px;
            cursor: pointer;
        }
    </style>

    <script>
        export default {
            state: { captured: false },
            onMounted() {
                Capacitor.Plugins.SplashScreen.hide();
                this.update();
            },
            async takePhoto(e) {
              const { Camera } = Capacitor.Plugins;

              try {
                const photo = await Camera.getPhoto({
                  resultType: "uri"
                });
                
                if (!photo.webPath) return;

                this.state.imageUrl = photo.webPath;
                this.state.captured = true;
                this.update();
              }
              catch (e) {
                console.warn('Maybe user cancel it. ', e);
              }
            },
            onAgain(e) {
                this.update({ captured: false })
            }
        }
    </script>
</my-app>

También debemos crear bajo la carpeta src un archivo index.js que será usado como script principal para armar el paquete con Webpack. Colocamos el siguiente contenido:

import { component } from 'riot'
import Tag from './my-app.riot'

component(Tag)(document.getElementById('app'), {
  title: 'Welcome!'
})

Para generar nuestra versión distribuible ejecutamos:

npx webpack
npx cap serve

Puedes ver el resultando en el navegador consultando: localhost:3333

Si tienes instalado y preparado Android Studio, para hacer la prueba en Android haces lo siguiente:

npx cap copy
npx cap open android

Ten presente que debe haberse generado previamente el proyecto para android con el comando: npx cap add android


© 2021 by César Arcila