SwiftUI por baixo do capô

Pietro Pugliesi
Academy@EldoradoCPS
5 min readJul 9, 2020

Se você é um desenvolvedor iOS, já deve ter ouvido falar do SwiftUI: um framework apresentado pela Apple em 2019 como um meio alternativo de construir apps: utilizando programação reativa.

Mas ao tentar aprender logo nos deparamos com coisas como @, Bindings, e $: o que significa tudo isso?

PropertyWrappers

Property wrappers são definições de política de dados, ou seja, o que queremos (quais capacidades) que uma variável tenha. Uma variável marcada com @propertyWrapper antes dela (os @State, @Binding, etc antes das vars) é um método de dizer que essas variáveis têm as propriedades e capacidades do propertyWrapper State, Binding, etc. Além disso, com os property wrappers podemos adicionar ou alterar essas funcionalidades implícitas das structs (são implícitas pois todas essas propriedades são encapsuladas na struct, e marcamos as variáveis que queremos que tenhas essas propriedades com a anotação @nomeDaStruct)

Assim, podemos colocar @Printable antes da variável username, e isso faz com que ela tenha as propriedades da struct Printable (essa struct marcada com a anotação @propertyWrapper). Há também um exemplo em que o wrapper deixa o texto em letras maiúsculas disponível aqui. Nesse caso, a diferença seria no set do wrappedValue: colocar wrappedValue = wrappedValue.capitalized

Vamos analisar com calma o que acontece nesse exemplo:

  1. definimos a struct Printable com a variável text do tipo String
  2. inserimos a anotação @propertyWrapper porque queremos que uma variável contenha as propriedades dessa struct
  3. o Swift nos pede para inserirmos a variável wrappedValue pois o valor da variável marcada com a anotação @Printable é passado para ela quando usamos essa variável anotada
  4. no get da wrappedValue fazemos o print e retornamos text. Ou seja, ao acessarmos wrappedValue (get), ocorre o print e pegamos o valor de text.
  5. criamos a struct User com a variável username, e essa anotada com @Printable, porque queremos que ela tenha as propriedades desse wrapper
  6. agora criamos um objeto user de username medium. Porém, o Swift acusa que não pode converter string medium para @Printable. Isso ocorre porque ele espera um argumento do tipo Printable na struct User, e passa medium como valor do wrappedValue.
  7. para resolver, definimos o init(wrappedValue: String) que aceita o valor de String para o wrappedValue, e nesse init passamos o valor de wrappedValue (o valor da chamada da variável anotada) para o atributo text da struct Printable
  8. Agora, somente por darmos get na user.username, ocorre um print de "wrappedValue →medium".

Resumindo, os propertyWrappers servem para nossas variáveis marcadas com essa anotação poderem usar dos métodos disponíveis pelas structs (ou classes) que definem esses propertyWrappers. Com as próximas explicações e exemplos vai ficar mais claro.

Bindings

O SwiftUI utiliza-se de programação reativa, isto é, se temos uma interação do usuário com uma View, não precisamos (nem devemos) escrever o que o programa faz a cada interação, pois as variáveis do programa reagem às mudanças na interface, e vice-versa. Um exemplo:

exemplo de Binding com @State

É um simples programa que copia o texto digitado em tempo real para a String meu texto. Note que não precisamos mandar o texto do Text(antigo UILabel do UIKit)ser atualizado a cada mudança no TextField, isso já é feito através do Binding.

Note que Binding pode ser traduzido como "ligação" ou "ligando", e é o que fazemos com o $text passado para o TextField na linha13: ligamos o que o usuário escreve no TextField na nossa variável text, com o propertyWrapper @State(linha 9). Dessa forma, marcamos que essa variável pode ser usada para ligações. Assim, a cada mudança nessa variável (na digitação do usuário), o SwiftUI redesenha as views que mudaram (no caso, só o Text) com esse novo valor da variável.

Bindings, fontes de verdade e $

Agora, temos um problema: existem vários tipos de propertyWrappers disponíveis: @State, @Binding, @ObjectBinding, etc. Por que isso?

Um vídeo da WWDC de 2019 também explica, mas em linhas gerais, o nosso programa tem uma (e só uma) fonte de verdade (Source of Truth, ou SoT): alguma coisa da qual quem precisa retira o valor das suas dependências. Por exemplo, o valor de um Switch (ligado/desligado) depende de uma fonte de verdade. Além disso, tanto uma mudança no Switch deve alterar o valor nessa fonte (para termos consistência em futuros acessos) quanto uma mudança nessa fonte que não venha pelo Switch deve ser refletida no valor do Switch também.

No nosso programa, a fonte de verdade é a variável text, marcada com @State. A fonte de verdade é quem dita as mudanças, ou seja, as views da nossa interface devem responder a essas mudanças, e se redesenharem caso necessário. Por exemplo, o nosso Text responde às mudanças na SoT, e se redesenha.

Mas e se o programa cresce, e precisamos usar essa fonte em outras Views? Aí entra o @Binding: ele usa SoT de outra view, e a passamos para ele como parâmetro quando inicializamos essa segunda View. Há outros tipos também, e sugiro assistir ao vídeo para mais informações. Assim, se queremos compartilhar uma SoT podemos usar @Binding,@StateObject ou @ObservableObject, por exemplo.

Por último, o $. Ele serve para criarmos um Binding a partir da variável.

da WWDC de 2019, Data Flow Through SwiftUI: a dependência do botão com a variável isPlaying é criada com o $ na criação do PlayButton

Olhemos a documentação sobre o TextField, foco no text: Binding<String>:

documentação sobre o TextField. Detalhe no text: Binding<String>

Passamos como parâmetro no nosso exemplo um texto a ser mostrado no TextField (o title), e o texto que ele fornece ao nosso programa, o text: Binding<String>. Ou seja, para chamarmos a função

TextField(“insira o seu texto”, text: $text)

precisamos de um Binding no parâmetro text. Criamos ele a partir do $.

Resumindo: o Binding é uma ligação bilateral: qualquer alteração no elemento binded altera o valor da variável ligada a ele, e vice-versa; e o $ cria um binding a partir de uma variável.

Conclusão e referências

Espero com esse artigo ter dado uma ajuda a entender como essas novas sintaxes do SwiftUI funcionam, como @, Bindings, e $. Como referências de propertyWrappers, consultar esse outro artigo, e os vídeos da WWDC: Data Flow Through SwiftUI, Modern Swift Api Design, e os vídeos sobre SwiftUI da WWDC 2020 também.

--

--

Pietro Pugliesi
Academy@EldoradoCPS

Software Developer @ Eldorado Research Institute; former student @AppleDeveloperAcademy Campinas