Em Swift, Optional (ou para muitos, apenas os sinais ?
e !
) é uma solução segura para acessar valores não existentes. Conforme a documentação da Apple, Optional é um tipo que representa um valor empacotado (wrapped) ou nulo (nil, ausência de um valor).
Optional é basicamente um enum e essencialmente se parece com isso
enum Optional<T> {
case none
case some<T>
}
Onde T é um tipo genérico como Array ou Dicionário. Mas o tipo Optional é tão importante que possui muitas sintaxes especiais que outros tipos não possuem. Algumas são:
- O case sem valor recebe a palavra reservada nil
- O sinal
?
é utilizado para declarar um Optional - O sinal
!
serve para desembrulhar (unwrap) o valor associado
Forced Unwrapping
Neste desempacotamento (unwrap) o sinal !
é utilizado diretamente para recuperar o valor associado. Não há nenhum tipo de validação para assegurar que o valor realmente existe. Basicamente, estamos dizendo que temos certeza que há algo para ser extraído. Esse é o método não seguro e não recomendado, uma vez que a variável pode estar vazia e um erro fatal será exibido e o aplicativo será fechado. Este é um erro admissível.
var driverLicense: Int? // declara a variável
driverLicense! // retorna erro
driverLicense = 1234 // associa valor
driverLicense! // retorna 1234
Optional Binding
Diferente do Forced Unwrapping
o Optional Binding
é a forma mais eficaz e segura para extrair o valor de uma variável ou constante. Permite verificar se o optional contém valor antes de utilizá-lo. Quando há valor este pode ser utilizado temporariamente dentro da declaração.
A verificação pode ser feita utilizando qualquer declaração condicional. O Optional Binding
é mais recomendado que os demais métodos.
let hello: String? = "Hello"
switch hello {
case .some(let data): print(data)
case .none: // retorna erro
}
Utilizando declaração if
if let greeting = hello {
print(greeting)
} else {
// retorna erro
}
Caso necessário alterar o valor consultado dentro da declaração pode-se utilizar o if var
if var greeting = hello {
print(greeting) // retorna *"Hello"*
greeting = "Olá" // altera valor da variável
print(greeting) // retorna *"Olá"*
} else {
// retorna erro
}
Tanto com if let quanto if var o valor somente existe dentro do escopo da declaração. Portanto sua mudança não tem efeito fora do bloco de validação. A alternativa para armazenar o valor extraído e ainda utilizá-lo fora do escopo é substituir a sintaxe if para guard.
Utilizando declaração guard
A declaração guard
é simples e muito poderosa. Ela realiza verificação da condição e, se o valor for nil, a instrução else
será executada e sairá do método. Se houver valor a informação desembrulhada é armazenada e pode ser acessar diretamente sem a necessidade de um novo unwrap.
guard let greeting = hello else { return }
print(greeting)
Da mesma forma do if var podemos substituir por guard var quando é necessário alterar valor unwrapped.
guard var greeting = hello else { return }
print(greeting) // retorna *"Hello"*
greeting = "Olá"
print(greeting) // retorna *"Olá"*
Implicitly Unwrapped Optionals
Implicitly Unwrapped Optional
é outra forma de desembrulhar valores. Apenas use o sinal !
após o tipo da declaração. Este método é utilizado quando temos certeza que o optional
possui valor, neste caso não teria necessidade de desembrulhá-lo sempre que for acessá-lo.
Ao acessar a variável/constante utilizando a forma implícita, mesmo que não tenha nenhum valor atribuído, o retorno será bem-sucedido. Mas cuidado, se tentarmos manipular essa informação invocando algum método, por exemplo, o retorno será um erro fatal.
var greeting: String!
print(greeting) // retorna *nil*
print(greeting.count) // FATAL ERROR. Terminated by signal 4
Nil Coalescing
Maneira de definir um valor padrão quando o Optional for nil. Há algumas formas de fazer esta ação como declaração condicional e operador ternário. Mas o Nil Coalescing nos permite encurtar isso ainda mais com os sinais ??
.
var x: String?
x = "Testing"
let y = x ?? "foo"
print(y)
Dessa forma o método print retornará “Testing”. Se a variável x não houvesse valor associado o print retornaria “foo”.
Optional Chaining
Optional Chaining
é o recurso que permite chamar propriedades e métodos em um Optional que pode ser nulo. Ao contrário do Implicitly Unwrapped
, o Optional Chaining
não retorna um erro fatal quando o valor é nulo.
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
let roomCount = john.residence!.numberOfRooms // FATAL ERROR. Terminated by signal 4
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.") // retorna aqui
}
Visualmente esta é a melhor forma de unwrap e também recomendada.