Cómo crear WORDLE app en SWIFTUI con la arquitectura MVVM
Aprende a crear el juego Wordle en SwiftUI. Te guío paso a paso para crear el modelo de datos, vistas y ViewModel en Xcode. En tan solo 30 minutos crearás un juego que fue vendido por más de 1 millón de euros.
Arquitectura MVVM en SwiftUI
Hoy en SwiftBeta vamos a aprender a cómo crear la app Wordle. Vamos a centrarnos en el juego, es decir, en cómo está construido y vamos a replicarlo. Vas a ver que en tan solo unos minutos vas a poder crear tu propio Wordle usando Xcode, SwiftUI y Swift.
Wordle en SwiftUI
Creamos el Modelo
Lo primero de todo que vamos hacer es crear dos vistas. La primera vista es la parte del juego (las celdas distribuidas en un Grid de 5x6), donde el usuario añade las palabras. Y la segunda parte es el teclado.
Para poder representar la información que se muestra en estas vistas, vamos a crear un fichero llamado Model. Dentro de Model vamos a crear un enum llamado Status, este enum va a tener distintos estados para poder representar que:
La letra que hemos añadido aparece en la palabra
La letra que hemos añadido aparece y está en la posición correcta
La letra que hemos añadido no aparece en la palabra
Color de las celdas del juego y teclas del teclado
El código quedaría de la siguiente manera:
A continuación creamos una struct llamada LetterModel para añadir la letra que se mostrará en cada celda del grid y también en las teclas de nuestro teclado. Estas celdas y teclas tienen que saber que estado tienen, es por eso que vamos a crear una propiedad con el tipo Status que acabamos de crear, es decir:
Ahora vamos a crear dos propiedades computadas que nos ayudaran a saber qué color poner en el texto y en el background de la celda y la tecla. Nuestra struct LetterModel quedaría de la siguiente manera:
Ahora solo nos queda construir un Array de LetterModel que representará los datos necesarios para crear nuestro teclado.
Esto es solo el modelo de datos, la información que necesitamos para representar de alguna manera nuestra vista. Es decir, este array que contiene la variable keyboardData lo podríamos representar visualmente de muchas maneras, pero nosotros usaremos un Grid para darle el aspecto de un teclado.
Creamos la vista KeyboardView
Lo siguiente que vamos hacer es usar un LazyVGrid en SwiftUI para crear el Grid que queremos y simular el aspecto de un teclado:
Podemos ver en el Canvas cómo queda nuestro teclado. Hemos creado 10 columnas con un GridItem flexible de mínimo 20.
Ahora ya tenemos la vista inferior. Más tarde volveremos a ella para conectar con nuestro ViewModel (el que crearemos más adelante).
Creamos la vista GameView
Ahora vamos a centrarnos en la vista superior del juego, en la que el user añade las letras en un Grid. Para hacerlo vamos a seguir el mismo proceso que antes, vamos a crear una variable llamada gameData, y esta variable va a representar las filas de nuestro Grid (de nuestro tablero).
A continuación vamos a crear un LazyVGrid, tal y como hemos creado para nuestro teclado:
Dentro del LazyVGrid creamos un ForEach para saber en qué columna estamos y así poner la posición correcta los datos de la variable gameData.
Una vez hemos creado el Modelo y las vistas, ahora vamos a unir la vista GameView y KeyboardView en nuestro ContentView.
Unir GameView y KeyboardView en ContentView
Para mostrar las dos vistas en ContentView, vamos a utilizar un VStack, tan sencillo como hacer lo siguiente:
Una vez hemos creado nuestras vistas, lo siguiente que vamos a crear es el ViewModel. El responsable de crear toda la lógica de nuestro juego.
Creamos el ViewModel
Creamos nuestro ViewModel y añadimos las siguiente propiedades:
numOfRow nos servirá para saber en qué fila estamos de nuestro Grid
word nos servirá para hacer comprobaciones a la palabra que está insertando un user
gameData, representación de nuestro Grid, de nuestro tablero de juego. Al inicializar el juego este tablero está vacío, pero a medida que un user añada palabras se irá rellenando.
Fíjate que ya teníamos una variable llamada gameData que habíamos creado en la vista de GameView. PERO ya no la necesitamos, es decir la puedes borrar, ya que el ViewModel será el responsable de tener esta información (y de modificarla!).
Lo siguiente que vamos hacer es añadir un método llamado addNewLetter y aceptará un parámetro que será de tipo LetterModel:
Este método como bien has deducido lo vamos a conectar con nuestro KeyboardView:
Si el button es un cohete llamará a un método que aplicará cierta lógica
Si llama a la cesta de la basura llamará a otra lógica completamente diferente
Si no es ninguna de las teclas anteriores, lo que hará será almacenar la nueva letra en la casilla correcta de nuestro Grid.
Para que el compilador no se queje, vamos a crear los dos métodos:
tapOnSend()
tapOnRemove()
Y ahora vamos a conectar este método addNewLetter con nuestra vista KeyboardView cada vez que se pulse una tecla, lo único que tenemos que hacer es crear una instancia de ViewModel y llamar al método addNewLetter cada vez que se pulse una tecla:
Arreglamos también la preview de nuestra vista:
Ahora, si intentamos compilar nuestra app fallará, ya que KeyboardView espera una instancia de ViewModel. Vamos a arreglarlo creando e instanciando una propiedad de ViewModel en ContentView:
Vamos a compilar nuestra app y vamos a ver qué hemos conseguido. De momento tenemos la vista bien estructurada, y cuando pulsamos una tecla de nuestro teclado se printa su valor por consola, diferenciando 3 casos:
Se ha pulsado la tecla de 🚀
Se ha pulsado la tecla de 🗑
Se ha pulsado cualquier otra tecla.
Ahora vamos a continuar añadiendo código en nuestro ViewModel. Lo siguiente que vamos hacer es crear una propiedad con la palabra que estamos buscando, en nuestro caso vamos a asignarle el valor de "REINA".
y también vamos a añadir un método que nos va a indicar si una palabra existe o no.
Si intentamos compilar ahora fallará, debemos importar en nuestro ViewModel el framework UIKit
Lo siguiente que vamos hacer es rellenar el método de tapOnSend(), dentro de este método vamos a aplicar la lógica más importante de nuestra app.
Para poder ver los cambios en nuestra vista GameView debemos crear una propiedad de tipo ViewModel. Tal y como hemos hecho con KeyboardView.
También, en nuestra vista ContentView debemos pasarle la instancia que hemos creado de ViewModel y pasarsela a GameView.
Vamos a compilar, y vamos a probar nuestra app.
¡Perfecto! vemos como al pulsar una tecla se modifica nuestro Grid, nuestro tablero. Ahora vamos a rellenar un método para poder eliminar letras de una palabra. Para ello, volvemos a nuestro ViewModel y rellenamos el método tapOnRemove()
Creamos la pantalla de error o victoria
En nuestro anterior código printabamos un error por consola, lo que haremos a continuación es mostrar un banner, es decir, una vista indicando qué error ha ocurrido y también marcaremos en rojo las celdas del Grid, para que sea más fácil de visualizar dónde tenemos el error. También vamos a aprovechar y vamos a añadir el caso de que un user gane la partida.
Para hacerlo, nos vamos a nuestro ViewModel y creamos el siguiente enum:
Y dentro de la clase ViewModel vamos a crear una propiedad nueva de tipo BannerType
Ahora, cada vez que tengamos un error, vamos a asigna a esta propiedad el case error con un mensaje. Es decir, el método tapOnSend() nos quedaría de la siguiente manera:
Ahora solo nos falta crear un método nuevo para saber cuando tenemos un error, y este nuevo método lo llamaremos desde la vista GameView.
Antes de irnos a la vista, vamos a añadir la siguiente línea del código justo al principio del método addNewLetter:
de esta manera si se pulsa una tecla nueva desmarcamos todas las celdas del Grid que estaban en rojo.
Y ahora si, vamos a llamar al método hasError desde GameView, así cuando obtengamos un error cambiaremos el color del texto y del background en el Grid, es decir:
Fíjate que hemos añadido una condición en el modificador .foregroundColor y .background.
Ahora lo único que nos falta es mostrar una vista para indicar a un user que error está obteniendo. Primero debemos crear la vista:
Y ahora vamos a llamarla desde ContentView y para hacerlo vamos a añadir un ZStack:
¡Perfecto! Ahora cada vez que tenemos un error estamos marcando la fila del Grid en rojo y también estamos mostrando un banner con el error específico. Ahora vamos a hacer un cambio muy simple para indicar que un user ha ganado.
Dentro de nuestro ViewModel, justo después de actualizar el teclado, añadimos las siguientes líneas que nos van a indicar si un user ha ganado o no, nuestro métodod tapOnSend debería quedar de la siguiente manera:
Si ahora compilamos nuestra app y la probamos, vemos como si acertamos la palabra, poniendo REINA, vemos como aparece el banner en la parte superior indicando que hemos ganado.
Conclusión
Hoy hemos aprendido a cómo crear el juego Wordle (que tanto está de moda) en SwiftUI. Hemos ido paso por paso, creando el modelo, las vistas y luego el ViewModel. En tan solo 30 minutos hemos podido replicar este juego.
Crear webs con SwiftUI es posible, en este post te enseño a cómo crear tu landing page con código 100% SwiftUI y Vercel
Vercel nos ayuda a deployar webs con mucha facilidad.
En iOS podemos usar CallKit para bloquear llamadas entrantes, en este post te explico la segunda parte para crear una app en SwiftUI, SwiftData y CallKit. Aprendemos a compartir la base de datos de la app con una extensión