Aún estamos terminando de poner en claro todo lo que hemos conocido en una de las WWDC más intensas y más cargadas de novedades que hemos tenido en los últimos años.
Pero seguro que muchos de vosotros se dieron cuenta que en los últimos 20 minutos de la presentación cambiaba el tono completamente. De una gala centrada en temas de usuario, se pasó a hablar de desarrollo puro y duro. En esta parte se presentó algo que es un primer paso revolucionario hacia el futuro del desarrollo de apps nativas en sistemas Apple: SwiftUI.
Vamos a explicarlo para que entendamos su importancia y el paso que es para Apple. Un paso que irá teniendo incidencia en las apps que usamos en el día a día en los próximos años.
Era una tranquila noche en la antigua Grecia...
Allá por el año 1979, Steve Jobs, Jef Raskin y otros cuantos afortunados ingenieros de Apple acudieron al Xerox PARC, buscando una evolución que no terminaba de llegar en el mundo de la microinformática. Los ordenadores habían comenzado a llegar a los hogares de la gente, pero que fueran una interfaz de texto con un lenguaje de programación, el BASIC en aquella época en los Apple II, no ayudaba a popularizarlos. Había que ir más allá.
En aquel viaje descubrieron el Xerox Alto, un ordenador con monitor en modo portrait que tenía una interfaz gráfica de usuario (algo que nadie había visto jamás) y un curioso aparato que controlaba una flecha llamado: ratón.
Los ingenieros de Xerox, comandados por Alan Kay, habían construido sin saberlo en un proyecto descartado y no entiendo de la compañía, el futuro de la informática. Pero no crearon solo el ordenador, porque sabían que hacer aplicaciones para una interfaz gráfica de usuario iba a ser muy complejo, así que también idearon su propio paradigma de desarrollo que revolucionaría también el mundo de la programación: el modelo MVC (model-view-controller) aplicado a la orientación a objetos.
En 1983 Apple lanzó el Lisa y Xerox el Alto, pero su alto precio (más de $18.000) mandaron ambos productos al fracaso hasta que en 1984 llegó el Macintosh. Este hizo dos cosas bien: la primera tener un precio más asequible (en comparación, pues eran $2.495). La segunda que venía con una serie de discos con guías de aprendizaje y unas cintas de cassette (sí, sí, cintas) que ponías en tu pletina, le dabas al PLAY y oías cómo se te guiaba paso a paso sobre cómo funcionaba y se manejaba la interfaz gráfica de usuario del Macintosh y las diferentes apps que venían en disco.
Desarrollar para ese ordenador era poco menos que un infierno. El equipo de Steve Jobs se dedicó solo a dar salida al producto, pero no a cómo crear software para ese producto. Hacer una aplicación para una interfaz gráfica se hacía programando gráficamente cada línea de cada botón, campo o elemento que se viera en pantalla: no se podía re-utilizar código, no existían las librerías (o frameworks) y cualquier elemento eran cientos o miles de líneas. Hacer que desarrollar software fuera fácil era el paso siguiente que Jobs hubiera dado tras el lanzamiento del Macintosh, pero como ya sabemos fue invitado a abandonar la empresa que co-fundó.
Así que fundó NeXT y allí aplicó el otro 50% del gran descubrimiento que hizo en el Xerox PARC: desarrollar la tecnología detrás de la creación de aplicaciones para interfaces gráficas de usuario de Alan Kay. El trabajo de todo el equipo de NeXT se lanzó en 1988 y supuso toda una revolución. Con la nueva aplicación Interface Builder y usando el lenguaje Objective-C, se podía coger un lienzo, arrastrar y soltar un botón, un campo de texto, una etiqueta, una casilla de check... cualquier elemento. Arrastrar y soltar. Luego se creaba una conexión entre el código y el elemento gráfico (un outlet) y ya podíamos trabajar con el objeto de ese elemento.
No solo eso: todo funcionaba con dos librerías que NeXT lanzó con todo el código necesario para construir apps y usar esos componentes, así como librerías básicas de trabajo con cadenas, fechas o diferentes tipos de datos: las librerías AppKit y FoundationKit. Aquel fue el momento en que el desarrollo cambió para siempre y fue de nuevo Steve Jobs quien capitaneó otro cambio disruptivo en la historia de la informática. Porque tampoco existían los frameworks hasta entonces.
Esta tecnología fue la responsable que Steve Jobs volviera a Apple en 1996, dado que la empresa que co-fundó se había quedado sin arquitectura de desarrollo (basada en Pascal) tras la compra por parte de Borland de la principal herramienta que se usaba para crear apps para MacOS. Y de ahí surgió en 2001 OS X y en 2007 la librería UIKit que permitió dar vida al primer iPhone.
Desde entonces nada ha cambiado en cuanto a arquitectura: podemos hacer más cosas, más rápidamente, mejor, incluso con un nuevo lenguaje como Swift. Pero la arquitectura de desarrollo, la forma en que hacemos las apps arrastrando elementos a un lienzo y creando un outlet que lo conecta al código es la misma. Los cimientos no han cambiado en más de 30 años... hasta ahora.
UIKit, interfaces imperativas
La arquitectura que se ha venido usando hasta ahora, está basada en lo que hoy se conoce como construcción de interfaces imperativas. Una construcción en la que creo una función y la asocio a un botón (por ejemplo). Creo una acción. Cuando alguien toque el botón, aquellas líneas que hay en el botón serán ejecutadas. Nada más simple.
Pero una interfaz imperativa tiene un problema que la hace menos eficiente: lo que llamamos el "estado". Básicamente aquellos valores que tenemos en nuestro código y que cuando tocamos deben tener un reflejo en lo que hemos tocado. Si yo pulso el botón, recibo una variable (un dato) que es el propio botón. Y a este, por ejemplo, le puedo cambiar el color o su texto por haber sido pulsado. Al cambiar esa propiedad de esa variable que es el botón, la interfaz tiene que reaccionar de forma inmediata y cambiar dicho color o texto. Estamos cambiando su "estado".
Cada posible valor que yo pueda darle a un elemento en la interfaz es un estado. Si tengo un campo que puede estar activo o no, tengo una propiedad de tipo Bool que será verdadera o falsa (solo tiene esos dos posibles valores). Verdadero está activo, falso no lo está. Dos estados. Pero también tengo otro propiedad de tipo Bool que es si el campo está o no oculto. Ya tengo cuatro posibles estados que gestionar desde la interfaz:
- Oculto sí, activo sí
- Oculto no, activo sí
- Oculto sí, activo no
- Oculto no, activo no
Cualquier elemento de la interfaz tiene cientos más de posibles estados para cada elemento: color, texto, color del borde, sombra, se toca o no... muchísimos. La complejidad es enorme y a mayor complejidad, más le cuesta a un sistema gestionar una interfaz llena de elementos. Básicamente porque tiene que estar "atento" a todos estos posibles cambios de estado y combinaciones para reaccionar ante ellos. Algo poco eficiente y muy propenso a errores.
La respuesta para hacer más eficientes a las interfaces llegó en los años 90 cuando se comenzaron a usar lenguajes de maquetación como el HTML, donde tenemos un ejemplo perfecto de interfaz declarativa. Una interfaz que se define como es y no cambia hasta que el usuario interactúa y se cambia a otra página. Pero la página HTML en sí, mientras la vemos, no cambia nunca. Es inmutable.
SwiftUI, interfaces declarativas
Obviamente, usar una interfaz declarativa como HTML, completamente inmutable, es poco práctico. Así que los desarrolladores se pusieron a trabajar para encontrar formas de trabajar con el estado, pero que cualquier posible cambio en este también estuviera declarado con anticipación. De esta forma, si un estado cambia por la interacción de un usuario, la interfaz sabrá qué tiene que hacer pero no lo cambiaremos nosotros con ningún código. Nosotros ya le dijimos qué tenía que pasar cuando la declaramos.
Vamos a verlo aún más claro: si la interfaz no sabe qué puede pasar con ella, tiene que estar pendiente de forma constante a millones de combinaciones de posibles cambios. Son un montón de observadores que están esperando eventos: que cambie un color, que se mueva un campo, que cambie un tipo de letra, que se mueva una imagen... todos los posibles cambios que puede tener una interfaz (todos sus posibles cambios de estado) están en permanente escucha y la interfaz tiene que estar preparada para representarlos.
Pero si hago una interfaz declarativa, yo estoy definiendo (antes que se pinte esta) qué estados tiene que observar y qué tiene que hacer cuando ocurra. Solo tendrá que estar pendiente de estos porque todo lo demás será inmutable. E incluso podrá procesar anticipadamente todas las combinaciones. Así que el coste de proceso de gestionar esa interfaz de usuario es infinitamente menor. Porque ya estamos declarando al construir la interfaz qué puede hacer y qué no: todas sus reglas. Así que solo tiene que estar atenta a estas, cumplirlas y sabe antes de ejecutarlos cuántas combinaciones posibles tendrá esa interfaz. Nada más.
Hoy día muchas librerías como Flutter de Google o React de Facebook, ya usan interfaces declarativas, y ahora Apple se ha subido al carro de esta tendencia en el desarrollo, de la mano de su lenguaje Swift con SwiftUI.
Gracias a SwiftUI, además, veremos la correspondencia en tiempo real entre nuestro código que declara la interfaz y su resultado. Usando sus diferentes componentes, esta interfaz se construirá y dibujará de forma automática para iPhone, iPad, macOS, watchOS y tvOS. Adiós a las temidas constraints, reglas que había que dar para que nuestra interfaz fuera capaz de dibujarse en cualquier dispositivo fuera cual fuera su tamaño. Ahora todo ello lo controla de forma automática el sistema como lo haría un navegador con HTML.
Por supuesto, tenemos las suficientes propiedades para usar, que nos permitan dibujar lo que queramos sin problema, animaciones incluidas, fuentes de datos dinámicos asociados a la interfaz, eventos... todo lo necesario.
Un pequeño ejemplo de estructura
Cuando creamos un nuevo proyecto en Xcode 11, lo primero que vemos es un nuevo check que nos dice si queremos crear un proyecto con SwiftUI y esto nos crea un proyecto con un nuevo delegado de escenas (que se usará, entre otros cosas, para la nueva función de múltiples copias de una misma app abiertas al mismo tiempo), pero no tendremos como es costumbre un Storyboard de inicio donde normalmente empezamos a crear nuestra interfaz.
En su lugar hay un fichero llamado ContentView
que desde la escena es usado para generar la pantalla de inicio con código. El funcionamiento, sin entrar en mucho detalle técnico, se basa en un struct
de Swift (una estructura) a la que le digo que es de tipo View
. Esto obliga a que se incluya una variable Body
que devolverá los diferentes constructores: el cuerpo de la pantalla.
struct ContentView : View {
var body: some View {
Text("Hello World")
}
}
Lo que hay dentro de body
es un constructor que crea un texto y lo pone justo en el centro de la pantalla. Así de simple. Si quiero que el texto sea en negrita, pongo un punto después del paréntesis y llamo a la función bold()
.
Text("Hello World").bold()
Si quiero meter más de un elemento tengo que usar agrupaciones, como un vista apilada. Con Vstack
creo una y pongo dentro lo que quiero.
struct ContentView : View {
var body: some View {
VStack {
Text("Hello World").bold()
Image(systemName: "book")
}
}
}
De esta forma pongo un vista apilada vertical con el texto encima y una imagen del nuevo conjunto de caracteres San Francisco Symbols, que representa un libro. Ahora le pongo un botón.
struct ContentView : View {
var body: some View {
VStack {
Text("Hello World").bold()
Image(systemName: "book")
Button(action: {
print("Toqué")
}, label: {
Text("Soy un botón")
})
}
}
}
Aquí le digo que quiero un botón y le paso dos parámetros como bloques de código (o closures): action
que es lo que hará al pulsarse el botón y label
que es aquello que se mostrará en el botón, un texto. Podría haber mandado una imagen y se hubiera hecho un botón de una imagen.
Esto es lo más simple y sencillo, obviamente. Solo un pequeño atisbo de todas las posibilidades que hay a la hora de construir interfaces. Si usamos macOS Catalina, podremos ver el lienzo al lado del código y añadir elementos arrastrando y soltando en la interfaz, viendo cómo queda y cualquier cambio en dicho constructor modificará el código en tiempo real. Igual que cualquier cambio en el código, cambia la interfaz al momento.
Un primer paso
Esta tecnología es un paso adelante sin precedentes, que ahora mismo funciona como una librería nativa en Swift que saca todo la ventaja del lenguaje y sus características, que se unen a otra librería de programación reactiva (para eventos asíncronos) llamada Combine
de la que hablaremos más adelante si os interesa.
Pero ojo, hay que ser realista. Crear una librería de construcción de interfaces desde 0 es una tarea épica, y Apple solo acaba de empezar y nos deja comenzar a usar y aprender (porque hemos de aprender casi desde 0) una nueva forma de hacer interfaces. Pero ahora mismo funciona como una capa puesta encima del anterior UIKit, no es independiente a nivel infraestructura. ¿Siempre será así? No, Apple irá independizando el motor. El mismo lenguaje Swift siguió ese proceso: en la primera versión Swift era una forma diferente de escribir Objective-C, que se traducía a este en muchos elementos y tipos de datos. Y ahora es completamente independiente en su arquitectura.
Estamos seguros que lo que ahora es una capa de la antigua librería UIKit, pero que permite una forma diferente y más práctica de hacer apps, pasará poco a poco a ir consiguiendo su propia arquitectura independiente, hasta serlo completamente de UIKit y crear una nueva librería que no olvidemos es compatible con todos los sistemas Apple.
Tal como pasó con Swift cuando fue lanzado en 2014, estamos echando un vistazo al futuro y ya podemos empezar a trabajar con él. Y volvemos a lo de siempre: Apple no ha inventado nada, todo esto ya estaba inventado. Pero ellos crean su propia versión con un objetivo claro: ser los que mejor lo hacen. Por ahora, parece que van por buen camino.
Ver 28 comentarios