DEV Community

David Goyes
David Goyes

Posted on

Swift #13: Closures

Un "Closure" es una función anónima definida como bloque de código con parámetros, valor de retorno e instrucciones. Su sintaxis es:

{ (parameter1: InputType1, ...) -> ReturnType in
  // statements
  return x
}
Enter fullscreen mode Exit fullscreen mode

La palabra clave in separa los tipos de datos de las instrucciones.

Se puede asignar un closure a una variable y ejecutarlo llamando el nombre de la variable, seguido de la lista de argumentos en paréntesis, con la diferencia que ya no van a tener etiquetas.

let sumar = { (first: Int, second: Int) -> Int in
  first + second
}
let result = sumar(1, 2) // 3
Enter fullscreen mode Exit fullscreen mode

Recibir un closure como parámetro en una función

Como puedo asignar un closure a una variable, también puedo pasarlo como argumento a una función. Para ello, el tipo del parámetro debe indicar los tipos de dato de los parámetros del closure y el tipo de dato de retorno. Por ejemplo:

let sumar = { (first: Int, second: Int) -> Int in
  first + second
}
let multiplicar = { (first: Int, second: Int) -> Int in
  first * second
}
func f(closure: (Int, Int) -> Int) {
  let result = closure(1, 2)
  print("El resultado es \(result)")
}
f(closure: sumar) // 3
f(closure: multiplicar) // 2
Enter fullscreen mode Exit fullscreen mode

Para pasar un closure como argumento de una función no es necesaerio asignarlo a una variable, sino que se puede pasar directamente al llamado de la función.

func f(closure: (Int, Int) -> Int) {
  let result = closure(1, 2)
  print("El resultado es \(result)")
}

f(closure: { (first: Int, second: Int) -> Int in
  first + second
}) // 3

f(closure: { (first: Int, second: Int) -> Int in
  first * second
}) // 2
Enter fullscreen mode Exit fullscreen mode

Inferencia de tipo

Así como Swift es capaz de inferir el tipo de un Int, Double o Bool también es capaz de hacerlo con un closure, siempre que tenga suficiente información contextual para hacerlo. Por ejemplo, en el ejemplo anterior, al pasar directamente un closure como argumento a una función, se puede omitir los tipos de los parámetros y valor de retorno, dado que el parámetro closure de la función ya había especificado el tipo (i.e. (Int, Int) -> Int).

Notar que:

  1. al quitar los tipos de datos de los parámetros del closure, ya no es necesario envolverlos entre paréntesis.
  2. La palabra clave in marca la división entre la lista de parámetros y las instrucciones.
func f(closure: (Int, Int) -> Int) {
  let result = closure(1, 2)
  print("El resultado es \(result)")
}

f(closure: { (first, second) in
  first + second
}) // 3

f(closure: { first, second in
  first * second
}) // 2
Enter fullscreen mode Exit fullscreen mode

Closure remolcado ("Trailing Closure")

Cuando el último argumento de una función es un closure, se lo puede declarar después de llamar a la función. Cuando se pasa el closure de esta manera, pierde la etiqueta del argumento (e.g. closure:). Por ejemplo:

func f(closure: (Int, Int) -> Int) {
  let result = closure(1, 2)
  print("El resultado es \(result)")
}

f() { (first, second) in
  first + second
} // 3

f() { first, second in
  first * second
} // 2
Enter fullscreen mode Exit fullscreen mode

Omitiendo los nombres de los parámetros

Además de poder inferir el tipo de los parámetros y valor de retorno, se puede omitir el nombre de los parámetros y, en lugar de ellos, referenciar cada parámetro por un índice numérico que empieza en 0, precedido del operador $ (e.g. $0, $1, $2, etc). Por ejemplo:

func f(closure: (Int, Int) -> Int) {
  let result = closure(1, 2)
  print("El resultado es \(result)")
}

f() { $0 + $1 } // 3

f() { $0 * $1 } // 2
Enter fullscreen mode Exit fullscreen mode

Top comments (0)