DEV Community

Pablo Terradillos
Pablo Terradillos

Posted on • Updated on

Sobre-analizando el hello world de SwiftUI, desde javascript

Hace unos días comencé a trabajar en un proyecto personal que, por motivos que comentare en otro post, espero, decidí desarrollarlo de forma nativa para iOS (ademas de su versión web, claro)

Por suerte para mí, en 2019, Apple anuncio SwiftUI, un framework que, en palabras de Apple:

"Ofrece vistas, controles y estructuras de diseño para declarar la interface de tus applicaciones".

Una definición, que para los que venimos del mundo web, nos puede sonar a la de React (y bueno, de varios otros frameworks tambien).

Lo cierto es que SwiftUI toma mucho de React y, en esencia, es muy parecido. Claro que, adaptado al ecosistema Apple y ofreciendo una experiencia muy similar a lo que, en el mundo web frontend, seria similar a usar React + Mobx + Storybook + algun "design system" (en este caso, el design system de Apple).

Comenzando

Comenzar con SwiftUI es bastante simple (obviamente, es requerimiento contar con macOS, a diferencia de la web, el ecosistema de Apple no esta pensado para ser abierto):

  1. Abrimos Xcode
  2. Seleccionamos "Create a new Xcode project" / "Crear nuevo proyecto de Xcode"
  3. Elegimos como template "Single View App"
  4. Llenamos un par de datos y, muy importante, seleccionamos "User Interface: SwiftUI"

Xcode inicializa el proyecto y vemos su pantalla principal y varios archivos creados. Por defecto, tendremos abierto "ContentView.swift", nuestra primera vista de SwiftUI.

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, World!")
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
Enter fullscreen mode Exit fullscreen mode

Esto ya podemos compilarlo y correrlo en un simulador o incluso en un dispositivo iOS/iPadOS/macOS. Ademas veremos a la derecha o debajo del editor, dependiendo del tamaño de la ventana, un preview de nuestra aplicación/vista (seguramente haya que hacer click en "resume" para comenzar a ver algo)
Pero veamos un poco que esta pasando acá.

La primer linea import SwiftUI es bastante clara, incluye dicha librería/framework.

Luego vemos un struct llamado ContentView que implementa un protocol de nombre View:

protocolos, clases estructuras, referencias, etc

Vamos por lo más sencillo: un protocol es, ni más ni menos, lo que en Typescript o Java es una interface. Es decir, un contrato, estamos diciendo que nuestra struct tiene una serie de atributos específicos. Javascript no posee tipos, por tanto no tenemos un equivalente directo más allá de usar un objeto y "confiar" (o chequearlo en runtime) que tendrá un determinado método o propiedad.

Sigamos por el struct: Esto equivale a una class en Javascript y una instancia de este struct equivaldría a un objeto. Pero hay una pequeña trampa.
En Swift las instancias de structs Siempre se pasan por valor.

¿Que quiere decir eso? que si pasamos nuestro objeto, mediante una llamada a una función o una asignación, este objeto se copiará y la función recibirá una nueva copia del mismo.
En Javascript, los objetos siempre se pasan por referencia, es decir, lo que pasamos en realidad es un puntero al espacio de memoria del objeto y no el objeto en sí.

Veamos:

let user = {
  name: "Pablo"
}

let anotherUser = user

anotherUser.name = "Juan"

console.log(user.name) // "Juan"
console.log(anotherUser.name) // "Juan"
Enter fullscreen mode Exit fullscreen mode

Mientras que en Swift:

struct User {
  var name: String
}

var user = User(name: "Pablo")
var anotherUser = user
anotherUser.name = "Juan"

print(user.name) // "Pablo"
print(anotherUser.name) // "Juan"
Enter fullscreen mode Exit fullscreen mode

Si bien no esta presente en el código que estamos analizando, Swift posee class, que podriamos decir es lo mismo que un struct pero cuyos valores son pasados por referencia (sí, de la misma forma que ocurre en Javascript). La sintaxis es prácticamente la misma y el ejemplo anterior podemos verlo simplemente reemplazando struct por class.

class User {
  public var name: String

    init(name: String) {
        self.name = name
    }
}

var user = User(name: "Pablo")
var anotherUser = user
anotherUser.name = "Juan"

print(user.name) // "Juan"
print(anotherUser.name) // "Juan"
Enter fullscreen mode Exit fullscreen mode

Como puede verse, también tuve que hacer dos cosas: especificar que el atributo name es publico (en las clases por defecto son privados) y definir un constructor (sí, el metodo init es similar al construct de las class de Javascript).

Pero volvamos al código inicial de SwiftUI. Como unica propiedad de este struct, tenemos a body. En este caso, los ":" (de var body: some view) nos indica el tipo de body... some View.
Esto podríamos leerlo literalmente: body es "alguna" View, no importa cual.
Nuevamente, en Javascript no tenemos nada parecido, porque no tenemos tipos. Pero pensando en Typescript o Java, podriamos preguntarnos: ¿Cual es la diferencia entre some View o directamente View siendo que View es un protocol?
La respuesta, es que some View es más similar a un tipo genérico (o generics). Al especificar some View estamos diciendo que esa variable es de un tipo especifico de View, no cualquier View.

Por ejemplo, el siguiente ejemplo es invalido:


protocol Greeter {
    func greet() -> String
}

class Person: Greeter {
    func greet() -> String {
        return "Hello"
    }
}


class User: Greeter {
    func greet() -> String {
        return "Howdy!"
    }
}

func test(a: Int) -> some Greeter {
    if a > 5 {
        return User()
    }

    return Person()
}
Enter fullscreen mode Exit fullscreen mode

En este caso, test intenta retornar un User o un Person, ambos implementan Greeter, pero al especificar que test retorna some Greeter, estamos diciendo que retorna un tipo especifico (en el ejemplo, podría ser un User o un Person, pero no ambos.
Si borramos la palabra some, el ejemplo compila correctamente.

Computed properties

Pero body sigue sorprendiendo, ya que directamente abre una llave que encierra un bloque de código.
Esto es lo que Swift llama "Computed properties", equivalentes a los getter y setters de Javascript. En este caso, al no especificar como asignar un nuevo valor a body, es simplemente un getter.

struct Person {
    var name: String
    var yearOfBirth: Int

    var age: Int {
        2020 - yearOfBirth
    }
}

var p = Person(name: "Pablo", yearOfBirth: 1987)

print(p.age) // 33 
Enter fullscreen mode Exit fullscreen mode

Lo que esta dentro de las llaves es simplemente una función. A Swift le encanta eliminar código redundante, por lo que en funciones de una sola linea, el resultado de dicha expresión es retornado (si hubiese más lineas, debería poner return 2020 - yearOfBirth).

Por último (¡al fin!), body retorna Text("Hello world"). Si hacemos "option + click" en Text, veremos que es una struct que implementa View (como era de esperarse dado que body es de tipo some View).

Views y Components

Podríamos decir que Text("Hello world") es un componente, como los componentes de React. SwiftUI sabe exactamente como mostrarlo, con que estilo y en que posición. De igual forma, existen varios componentes con distintos fines. Por ejemplo, encerremos nuestro Hello World en un List

struct ContentView: View {
    var body: some View {
        List {
            Text("Hello, World!")
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

y veremos como cambió nuestra aplicación.
También podríamos hacer nuestro Texto "clickeable" usando un Button.

struct ContentView: View {
    var body: some View {
        List {
            Button(action: {
                print("Hi!")
            }) {
                Text("Hello, World!")
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Ahora, cada vez que clickeemos (o tapeemos) nuestro texto, veremos "Hi!" en la consola de debug.

Casi todas las vistas tienen métodos para cambiar sus estilos. Por ejemplo, podemos hacer Text(...).fontWeight(.bold) para mostrar el texto en negrita.

Curiosidades varias

Etiquetas de parametros

Como habrás visto, al llamar una función en Swift, los parámetros indican el nombre. Swift permite definir etiquetas a los parámetros e incluso definir nombres distintos para la llamada y la implementación:

getAvatar(for: "Pablo")

func getAvatar(for user: String) {
  // ...
  // user -> "Pablo"
}
Enter fullscreen mode Exit fullscreen mode

Si en la definición omito el for, tendria que llamar getAvatar(user: "Pablo").

Funciones como último parametro

No se exactamente como se llama esto, pero algo curioso de Swift y que hizo que inicialmente me cueste leer el código, es el caso de Button más arriba:

  Button(action: {
    print("Hi!")
   }) {
     Text("Hello, World!")
    }
Enter fullscreen mode Exit fullscreen mode

¿Que es exactamente lo que rodea a Text?

Al igual que en javascript, Swift permite pasar funciones como valores y por tanto nuestras funciones pueden aceptar funciones como parámetros. Lo curioso, es que en aquellos casos que el último parámetro de la función sea otra función, podemos usar las llaves por fuera del paréntesis.

Como siempre, mejor un ejemplo:

func doSomething(value: Int, method: (Int) -> ()) {
  method(value)
}

doSomething(value: 5) { val in
  print(val) // 5
}
Enter fullscreen mode Exit fullscreen mode

Tambien podriamos llamar a doSomething siendo explicitos:

doSomething(value: 5, method: { val in
  print(val) // 5
})
Enter fullscreen mode Exit fullscreen mode

Conclusión

SwiftUI parte de una premisa muy similar a la de React: nuestra vista es una función de nuestro estado y las vistas se componen de otras vistas (De igual manera que los componentes se componen de otros componentes). Hacer una transición podría decirse que es relativamente sencilla y podemos re-usar mucho de dicho mundo.
Ademas, Xcode nos dara la suficiente ayuda para entender fácilmente que Views y que modificadores tenemos disponibles para armar nuestra aplicación. (cuando no falla, hay que decirlo).

Si llegaste hasta acá, te recomiendo que mires la introducción oficial de Apple a SwiftUI que seguramente es mucho más clara y extensa que este articulo: https://developer.apple.com/videos/play/wwdc2019/216/

Más adelante veremos como manejar estado dentro de SwiftUI usando el framework "Combine" de Apple (que, siguiendo con analogías, podríamos decir es similar a Mobx)

Si te interesa el desarrollo web y/o apps, ¡hablemos! Podes seguirme en twitter como @tehsis

Top comments (0)