Sobre-analizando el hello world de SwiftUI, desde javascript
Tags: swift, javascript, react, webdev
Published: 2020-08-04 13:47:34

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](https://developer.apple.com/documentation/swiftui): 

> "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](https://reactjs.org/)** (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](https://mobx.js.org/) + [Storybook](https://storybook.js.org/) + algun ["design system"](https://auth0-cosmos.now.sh/) (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*.

```swift
import SwiftUI

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

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

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`](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html) es, ni más ni menos, lo que en Typescript o Java es una [`interface`](https://www.typescriptlang.org/docs/handbook/interfaces.html). Es decir, un contrato, estamos diciendo que nuestra [`struct`](https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html) 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**](https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html).

¿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*](https://es.wikipedia.org/wiki/Puntero_(inform%C3%A1tica)) al espacio de memoria del objeto y no el objeto en sí.

Veamos:

```javascript
let user = {
  name: "Pablo"
}

let anotherUser = user

anotherUser.name = "Juan"

console.log(user.name) // "Juan"
console.log(anotherUser.name) // "Juan"
```

Mientras que en Swift:

```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"
```

Si bien no esta presente en el código que estamos analizando, Swift posee [`class`](https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html), 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`.


```swift
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"
```

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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)).

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)](https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html). 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:

```swift

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()
}
```

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"](https://docs.swift.org/swift-book/LanguageGuide/Properties.html#ID259), equivalentes a los [getter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) y [setters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set) de Javascript. En este caso, al no especificar como asignar un nuevo valor a body, es simplemente un getter.

```swift
struct Person {
    var name: String
    var yearOfBirth: Int
    
    var age: Int {
        2020 - yearOfBirth
    }
}

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

print(p.age) // 33 
``` 

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`

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

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

```swift
struct ContentView: View {
    var body: some View {
        List {
            Button(action: {
                print("Hi!")
            }) {
                Text("Hello, World!")
            }
        }
    }
}
```

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:


```swift
getAvatar(for: "Pablo")

func getAvatar(for user: String) {
  // ...
  // user -> "Pablo"
}
```

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:


```swift
  Button(action: {
    print("Hi!")
   }) {
     Text("Hello, World!")
    }
```

¿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:

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

doSomething(value: 5) { val in
  print(val) // 5
}
```

Tambien podriamos llamar a doSomething siendo explicitos:

```swift
doSomething(value: 5, method: { val in
  print(val) // 5
})
```

## 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](https://twitter.com/tehsis)