eskiuv0.14.1
eskiu-lang/ui · v0.3.0 · preview

Apps nativas, escritas en Eskiu

Escribe la interfaz una vez, como funciones de Eskiu. El mismo código dibuja vistas de UIKit en iOS, de android.view en Android y el DOM en la web, y corre en un webview en macOS, Linux y Windows.

La misma app de viajes en Eskiu en iOS, Android y la web: una lista de lugares, la página de Oaxaca y la de Kyoto

Crea apps nativas para Android, iOS y la web

Un componente es una función normal de Eskiu. Llama a Column, Text y Button para armar la vista y guarda su estado en signals. La app se compila a código máquina en iOS y Android, y a WebAssembly en la web.

Los textos, botones, campos, switches, listas, modales y alertas son los controles de cada plataforma, con su accesibilidad.

import <string>;
import <ui>;

void Counter() {
    Column(Style{padding: 24, gap: 12}) {
        *Signal<int> count = use_signal<int>(0);
        String label;
        defer String_free(&label);
        String_format(&label, "Pressed %d times", count.get());
        TextWith(Style{font_size: 28, font_weight: 700}, label.data);
        Button("Press me") {
            count.set(count.get() + 1);
        }
    }
}

Escrito en Eskiu, dibujado por la plataforma

Los componentes, el estado y las reglas de layout son el mismo código de Eskiu en todas partes. En cada frame, el núcleo le manda a la plataforma un buffer con los cambios, y un host delgado los aplica a vistas reales acomodadas por Yoga, el motor que usa React Native.

iOSUIKit, desde Objective-C++
Androidandroid.view, desde Java y JNI
WebEl DOM, desde WebAssembly
macOSEl host web en una ventana de WKWebView
LinuxEl host web en WebKitGTK
WindowsEl host web en WebView2
Una app de tareas en Eskiu en iOS y Android: una tarjeta de progreso, filtros y una lista de tareas con etiquetas de colores

Actualizaciones que tocan solo lo que cambió

Cada bloque registra los signals que lee. Cuando uno cambia, solo ese bloque se vuelve a ejecutar y sus hijos se emparejan por llave, así una lista conserva el estado de cada fila cuando se reordena. Las listas virtuales montan solo las filas cerca de la pantalla, así que un millón de filas cuesta lo mismo que una pantalla llena.

Los hooks, las llaves y los memos se revisan mientras la app corre, y un error se detiene diciendo dónde ocurrió: in Column[0] > TodoRow[key 3].

Una excepción no cierra la app. Un render que lanza una muestra el fallback del ErrorBoundary más cercano, y nada de ese render llega a la pantalla. Un handler o un efecto que lanza una se reporta, y la app sigue.

8.1 mspara montar 10,000 filas
5.9 mspara invertir las 10,000
1.2 mscuando cambia un signal que leen todas las filas
0asignaciones pendientes tras 10,000 montajes y desmontajes

Cada cambio en pantalla en un tercio de segundo

ui serve vigila la app y los paquetes de los que depende, y vuelve a compilar cuando cambia un archivo. En la web la página se recarga y conserva su URL y sus datos guardados. En el simulador y en Android la app que ya está abierta arranca otra vez con el código nuevo en el mismo proceso, sin instalarla ni lanzarla de nuevo.

Si un build falla, los errores del compilador se ven encima de la app hasta que el siguiente compila.

0.3 sde guardar al cambio en el simulador de iOS
0.4 sen un emulador de Android
0.6 sen el navegador
0reinstalaciones, salvo que cambie el código nativo
$ ui serve ios
ui: built in 2.4 s
Watching for changes
ui: changed, building
Restarted the app on the new build
ui: built in 0.3 s

# medido con la galería del kit en una Mac con Apple silicon

Overlays, selectores y avisos

<ui/kit> es un conjunto de componentes con tema hechos con los mismos elementos: tarjetas, botones, campos, checkboxes, pestañas y barras, y lo que se abre encima del contenido. Un Popover se coloca junto a lo que lo abrió, arriba o abajo, dentro de la ventana. DatePicker, Select, DropdownMenu, ColorPicker y Tooltip están hechos sobre él, y kit_toast muestra un mensaje por unos segundos.

El tema sigue la apariencia clara u oscura del sistema, o la que tú elijas.

El planificador de viajes de la app Explore: un calendario abierto bajo el campo de fecha en iOS, el menú de viajeros abierto en Android y un aviso de que el viaje a Kyoto se guardó en la web

Movimiento y gestos

Un estilo puede mover, escalar, girar y desvanecer un elemento, y transition_ms anima cada cambio en la plataforma: animaciones de UIKit, el animador de vistas de Android y transiciones de CSS. Una de las curvas es un resorte.

ui_on_pan reporta un arrastre con su distancia y su velocidad, y se lo quita a un botón de adentro en cuanto el dedo se mueve. use_animated lleva un número a su nuevo valor, para lo que la plataforma no puede animar. SwipeToDelete del kit está hecho con los dos.

Las animaciones corren con el reloj del display (CADisplayLink, Choreographer, requestAnimationFrame): pueden repetirse, ir y volver, encadenar keyframes y avisar cuando terminan. use_frame le da el mismo reloj a un game loop, y el paquete canvas dibuja sprites con él.

*Signal<int> dx = use_signal<int>(0);
*Signal<bool> held = use_signal<bool>(false);
ui_on_pan(void(Pan p) {
    held.set(p.phase == PanBegan || p.phase == PanMoved);
    dx.set(held.get() ? p.dx : 0);
});
Row(Style{translate_x: dx.get(),
          transition_ms: held.get() ? 0 : 250,
          easing: EaseSpring}) {
    Text("Arrástrame");
}

Los servicios del teléfono, con una sola API

Lo que una app le pide al sistema tiene un solo nombre en todas las plataformas, y cada host responde con el servicio propio de la plataforma.

Claro y oscuroui_color_scheme() y los temas del kit siguen al sistema cuando cambia.
AccesibilidadEtiquetas para VoiceOver, TalkBack y lectores de pantalla, el tamaño del texto, menos movimiento y anuncios.
IdiomaEl idioma del usuario y si se lee de derecha a izquierda.
Redui_fetch con NSURLSession, HttpURLConnection y el fetch del navegador.
Datos guardadosValores pequeños que sobreviven a la app, y un directorio de datos para archivos.
Compartir y portapapelesLa hoja para compartir del sistema, copiar y pegar.
HápticosVibraciones de selección, impacto y notificación, y una vibración en la web.
Temporizadoresuse_interval y use_timeout, con el reloj de la plataforma.
Teclado y márgenesEl área segura y el teclado, o la pantalla completa de borde a borde; un campo enfocado se desplaza a la vista.
Ciclo de vidause_app_state avisa cuando la app pasa a segundo plano y cuando regresa.
Pantalla de arranqueUn color y una imagen desde ui.toml, que se mantienen mientras cargan las fuentes y el estado guardado.
Datos de la appLa versión y el build, y valores de [env] que cambian entre desarrollo y las tiendas.
Archivos en cachéUna imagen o un sonido remoto descargado una vez y guardado como archivo entre arranques.

Código nativo cuando lo necesites

Una app o un paquete trae código por plataforma en native/: Objective-C o Swift en iOS, Java, Kotlin o C en Android, JavaScript en la web. Eskiu lo llama con funciones extern, el código nativo responde con eventos y puede registrar sus propias vistas para NativeView.

Las bibliotecas de terceros se declaran en ui.toml: las dependencias de Maven se compilan con Gradle y los paquetes de Swift con Xcode, y un paquete lleva las suyas a cada app que lo usa. Así están hechos los paquetes de ubicación, cámara, escaneo de códigos, conteo de pasos, almacenamiento seguro y dibujos SVG.

# ui.toml
[app]
name = "Explore"
scheme = "places"

[ios.spm]
Collections = { url = "https://github.com/apple/swift-collections", from = "1.1.0" }

[android]
dependencies = ["com.google.android.material:material:1.12.0"]
permissions = ["android.permission.CAMERA"]

Del editor a las tiendas

ui open ios y ui open android generan los proyectos de Xcode y de Android Studio de la app y los abren ahí. Cada Run compila primero el código Eskiu, así que un cambio en un archivo .esk está en el siguiente Run, con el depurador nativo, los profilers y tu propio teléfono.

ui release compila para el App Store y Google Play: un IPA firmado para tu equipo y un App Bundle firmado con una llave de subida que crea y guarda por ti. Revisa el ícono y el id de la app antes de compilar, toma el número de build del reloj y con --upload manda el build de iOS a TestFlight. Para el escritorio y la web hace un .app firmado, un binario de Linux y un .exe de Windows, cada uno con un archivo para distribuir, y un zip del build web para desplegar.

# ui.toml
[app]
name = "Explore"
id = "com.tuempresa.explore"
icon = "icon.png"
version = "1.0.0"

# Xcode, y cada Run compila el código Eskiu
$ ui open ios

# el App Store y Google Play
$ ui release
ios: build/ui/ios/release/Explore.ipa
android: build/ui/android/release/Explore.aab

De cero a una app corriendo

La herramienta ui crea un proyecto y lo compila o lo corre en cada plataforma. Necesita emscripten para la web, Xcode para iOS y el SDK de Android para Android, y en el escritorio un compilador de C con WebKitGTK en Linux o MinGW-w64 o MSVC en Windows.

Cada elemento, hook y servicio está en Componentes y APIs.

Para empezar con una app completa, ui-starter trae pestañas, pantallas con links, una lista desde la red y un tema guardado. Para un paquete con código nativo, empieza con ui-package-template.

git clone https://github.com/eskiu-lang/ui
eskiuc ui/tools/ui/main.esk -o ~/.eskiu/bin/ui

ui new myapp && cd myapp
ui serve          # o ui serve ios, android, macos

ui está en preview. Las vistas se escriben con trailing blocks: Column(Style{padding: 16}) { ... } pasa el bloque como último argumento de la llamada. Requiere Eskiu 0.14 o más nuevo.

Crea tu primera app.

Las guías cubren el estado, los componentes, la navegación, la plataforma y el código nativo.