Concrete Logo
Hamburger button

Unwrapping Optionals  –  Swift

  • Blog
  • 15 de Maio de 2016
Share

A Swift foi criada para ser uma linguagem de programação “Type Safety“, ou seja, o compilador conhece completamente o objeto declarado. Isso permite que ele possa tomar algumas decisões de como compilar o código, porque tem mais informações sobre ele. Partindo dessa definição, na declaração de variáveis ou constantes, diferente do Objective-C, você não pode declarar uma variável como nula, ela tem de ser opcional.

Quando utilizamos esses valores em Objective-C, sempre havia (ou deveria haver) a necessidade de checar se um valor é nulo ou não quando acessávamos essa variável. Para evitar a sempre confusa variedade de NSNotFound, NSNULL, nil, NULL etc, a Apple resolveu facilitar a nossa vida (raro) deixando todo valor ser nil-able type. Podemos fazer essa checagem de forma simples :

Um problema que passei ao migrar do Objective-C para Swift é que o compilador não deixa isso claro. Quando você tenta acessar um valor opcional, o compilador sugere que você utilize o !, “implicitly-unwrapped optionals“, matando assim um Boto. Sim, toda vez que você usar “implicitly-unwrapp” você mata um Boto, está na documentação da Apple, acredite.

Usar “implicitly-unwrapped optionals” é válido, mas apenas quando você tem plena certeza do que está fazendo. Já já chegamos lá.

Um exemplo simples em Objective-C :

Traduzindo esse código para Swift teríamos o seguinte:

O primeiro erro que o compilador indica é o seguinte :

“Value of optional type ‘SubItem?’ not unwrapped; did you mean to use ‘!’ or ‘?’?”

se você corrigir automaticamente, fica da seguinte forma :

print(item.subItem!.subItemName)

Eis o primeiro boto morto.

Porque evitar

Valores opcionais te fazem pensar em como agir caso algum valor não esteja nulo no ato de acesso, quando um JSON não vem corretamente do endpoint ou quando uma variável não foi setada ainda. Lembrando que se o valor for acessado e ainda for nulo, teremos um crash (o que poderia facilmente ocorrer no nosso código em Objective-C).

Como agir

Nesse caso, um simples if let para saber se a variável existe pode resolver nosso problema :

Parsing JSON

Vamos utilizar um exemplo um pouco mais complexo :

Objective-C

Traduzindo para Swift :

Uma olhada rápida no código e podemos reparar quantos ! podemos encontrar nesse parse simples. Esse costume de utilizar “implicitly-unwrapped-optionals” é ruim porque não nos permite utilizar um recurso muito bom em Swift, que é pensar, agir e tratar erros e exceções, evitando o crash. Vamos criar um caso no qual você recebe um JSON inválido, algum erro de rede que não era esperado. O uso do try! diz explicitamente que você confirmou ao compilador que era um JSON válido, porém… capote no app.

Como agir

Podemos utilizar um simples if..let, try? (Caso a expressão falhe ele retorna nil), o guard, cast opcional as? (Que também retorna nil caso falhe)

Primeiro, com a utilização do guard tentamos deserializar o JSON. Caso não seja bem sucedido, já temos nosso primeiro tratamento de erro, retornando um array vazio. Poderíamos utilizar um try..catch clássico e retornar um erro, porém no exemplo retornamos apenas um Array vazio. Para nosso código continuar, dentro da mesma condicional temos o cast opcional as?Array<NSDictionary>, para se acaso ambos ou um falhar, nossa função não irá continuar.

Pronto! Nossos requerimentos foram atendidos e temos um Array de NSDictionary. Vamos acessar propriedade a propriedade, sempre utilizando o cast as? caso a condição falhe.

No exemplo acima, podemos ver como utilizar “unwrapping-optionals” de um modo seguro, prevendo falhas e erros no processo.

Quando utilizar

Assim como eu, você deve ter se perguntado: se é tão ruim utilizar o !, porque o Xcode nos corrige tanto utilizando essa prática?

“Because the value of an implicitly unwrapped optional is automatically unwrapped when you use it, there’s no need to use the ! operator to unwrap it. That said, if you try to use an implicitly unwrapped optional that has a value of nil, you’ll get a runtime error.”

 

IBOutlets

Quando utilizamos IBOutlets, devemos assumir que eles estarão conectados no seu storyboard ou interfacebuilder e, quando seu código for executado, ele já estará inicializado. É um curto período de tempo após o init(), e caso ele não esteja conectado teremos o clássico erro:

fatal error : unexpectedly found nil while unwrapping an Optional Value

Assets

Se setarmos uma UIImage a qualquer UIImageView ou algo do gênero, nos deparamos com o seguinte código:

estamos mais uma vez dizendo ao compilador que o asset com nome “house” existe. Caso ele não esteja disponível no ato de acesso… já sabemos o crash que irá ocorrer. Uma opção caso você não tenha certeza se o asset estará disponível é utilizar o:

o operador ?? diz que, caso o asset “house” não exista, assumimos o valor de “thumbnail”, que é um valor default.

Dependency Injection

Caímos no mesmo caso dos assets, temos que assumir que seu arquivo .txt.json, está disponível dentro do projeto.

Casos Genéricos

Existem outros casos, quando utilizamos UITableViewCell, ou mesmo instanciamos um ViewController diretamente do Storyboard :

Nesses casos, quando você tem plena certeza que a opcional vai atender aos requerimentos o boto não morre, ele desmaia.

Conclusão

Unwrapping optionals são nossos amigos, e Swift foi feita para ser uma linguagem segura. Então, antes de você matar aquele Boto apenas para agradar o compilador, veja se não existe outra opção mais segura.

Ficou alguma dúvida ou tem alguma observação? Aproveite os campos abaixo!

É desenvolvedor iOS e quer fazer parte do nosso time? Acesse aqui.