Opcionais no Swift
Tags: ios, swift, mobile
Published: 2021-12-17 17:54:43

Você já viu um monte de códigos no Swift com "?" e "!" e fica perdido? Usa esses operadores mas sem saber direito quando e onde usar? Esse post então é pra você. 

## O que é um "?" no Swift
O símbolo "?" representa um opcional no Swift. E um opcional é (pasmem) um **enum**: .Some(Valor Encapsulado) e .None. Isso permite que ele possa ser vazio (.None) ou possa ter algum valor internamente (.Some(valor).

Exemplo:
``` Swift
var x: Int? = 10
```
Ao criar uma variável com "?", no caso **Int?** o Swift cria um enum do tipo **Optional<Int>** mas com o estado **.Some(10)**.
``` Swift
var x: Int? = nil
```
Caso contrário, se você atribui *nil*, é criada uma variável do tipo **Optional<Int>** com o estado **.None**

## Comparação
Sabendo agora como é armazenado um *Opcional*, o Swift oferece algumas formas de trabalhar com ele. Podemos comparar ele de algumas formas:
``` Swift
let x: Int? = 10

switch x{
   case .Some(let value) : print("Valor de x \(value)")
   case .None : print("x não tem valor")
}
```

Uma boa prática é "desembrulhar" (do inglês unwrap) o valor da váriavel de forma segura usando o guard. O guard pode ser executado da seguinte forma:
``` Swift
func foo(x: Int?){
   guard let y = x else{
      return
   }
   print(y)
}
```

## Encadeamento Opcional
O encadeamento opcional permite você pegar o valor de dentro de um opcional. Diferente do Unwrapping Forçado que vou falar a seguir, ele não causa um erro caso não tenha um valor dento do Opcional. Vamos ao exemplo:
``` Swift
var x: Int? = 10 // .Some(10)
var y: Int = x? // 10
```

No exemplo simples acima quando eu chamo **x?** eu faço um unwrapping do Opcional, ou seja, eu pego somente o valor que existe dentro dele. Vamos para um exemplo um pouco mais complexo. Considere o código:
``` Swift
class Person{
   var residence: Residence?
}

class Residence{
   var numberOfRooms = 1
}
```

Agora considere a seguinte função:
``` Swift
func foo(){
   let john = Person()
   if let roomCount = john.residence?.numberOfRooms{
      print("Número de quartos \(roomCount)")
   } else {
      print("Não foi possível determinar o número de quartos")
   }
}
```
O trecho de código acima não irá produzir um erro, pois o encadeamento opcional sempre retornar um Opcional que pode ser comparado. Mas ele será *nil*. Então sua condição irá cair no *else*. Mas se mudarmos aqui:
``` Swift
func foo(){
   let john = Person()
   john.residence = Residence()
   if let roomCount = john.residence?.numberOfRooms{
      print("Número de quartos \(roomCount)")
   } else {
      print("Não foi possível determinar o número de quartos")
   }
}
```
O retorno será **1**, pois é o valor padrão da quantidade de quartos. 

## Unwrapping Forçado
O unwrapping forçado tem uma sintaxe parecida, trocando apenas "?" por "!". Mas ele tem uma diferença fundamental: Caso o valor no Opcional seja *nil*, é gera um erro de execução e o aplicativo irá encerrar, caso não tratado. Utilizando o exemplo acima:
``` Swift
func foo(){
   let john = Person()
   if let roomCount = john.residence!.numberOfRooms{ //o app irá quebrar nessa linha
      print("Número de quartos \(roomCount)")
   } else {
      print("Não foi possível determinar o número de quartos")
   }
}
```
o unwrapping forçado só deve ser usado caso você tenha ABSOLUTA CERTEZA de que o Opcional tenha valor. 

## Coalescência
O operador de coalescência "??" permite que você retorne o primeiro valor não nulo (nil) de duas variáveis. Vamos deixar mais claro no exemplo:
``` Swift
let c: Int? = 10
let d: Int = 0
let e = c ?? d //o valor de e será 10
```
No exemplo acima prevalece o primeiro valor não nil. Segundo exemplo:
``` Swift
let c: Int? = nil
let d: Int = 5
let e = c ?? d //o valor de e será 5
```
Como no exemplo acima c é nil, o valor de e será 5, o primeiro valor não nil.

## BONUS ADVANCED: Map e FlatMap
O tipo Opcional ainda oferece duas funções chamadas Map e Flatmap que permitem que você controle o método de unwrapping passando um closure. A única diferença entro o Map e Flatmap é que o Map não pode devolver nil. Exemplos, exemplos:
``` Swift
var a : Int? = 10

let y : Int?  = a.Map{
   if $0 < 10 {
      return 0
   } else {
      return $0
   }
}

let z : Int? = a.FlatMap{
   if $0 < 10 {
      return nil
   } else {
      return $0
   }
}
```

É isso gente. Esse é todo o "mistério" dos Opcionais no Swift. A partir de agora você já sabe ler quando encontrar "?" e "!" nos códigos e sabe como posicionar nos seus próprios códigos.

Até a próxima! Keep coding!