Programmare in SwiftUI: l'oggetto View
Cos'è una View in SwiftUI: una struct che adotta un protocol con un solo requisito, il body, e come gli opaque type si differenziano dai protocol.
Tradotto dall’inglese con l’aiuto dell’IA e rivisto dall’autore.Leggi l’originale

Abbiamo visto che SwiftUI è un framework dichiarativo. Il risultato visivo che vediamo sul nostro dispositivo nasce da una serie di istruzioni con cui diciamo al compilatore come vogliamo vedere la nostra interfaccia. Ma se la UIView era il cuore pulsante di UIKit, qual è l'equivalente in SwiftUI? La risposta è un nuovo oggetto, chiamato View.
Che cos'è una View?
La prima vera grande differenza rispetto a una UIView è che in SwiftUI una View è una struct che adotta un protocol. L'esempio base fornito da Apple comincia così:
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
Quello qui sopra è un oggetto SwiftUI perfettamente funzionante, chiamato ContentView, che contiene (restituisce) nel suo body una label con "Hello World".
Noterete che ho parlato di "contenere" (return l'ho solo accennato tra parentesi). Uno dei cardini di SwiftUI è infatti la riusabilità del codice e il concetto di View riutilizzabili come se fossero mattoncini Lego ™.

Una view, quindi, definisce un pezzo della nostra User Interface.
Tutto in un'app SwiftUI può essere ricondotto a una View, che definisce un pezzo della nostra interfaccia. Di conseguenza, le view complesse sono costruite a partire da elementi semplici che cooperano tra loro, formando una gerarchia ad albero. Dietro le quinte, il compilatore gestisce tutte le View e le traduce in una struttura dati molto efficiente che mostra a schermo il contenuto richiesto. Ma come funziona questa composizione?
Un solo requisito: body
Abbiamo detto che una View è una struct che adotta il protocol View. L'unico requisito di questo protocol è fornire alla nostra View un body, responsabile del contenuto del nostro oggetto. Il body è una closure speciale che restituisce un contenuto anch'esso sotto forma di View. Per esempio, nel codice di Apple visto sopra:
...
var body: some View {
Text("Hello World")
}
...
In questo caso il body della nostra View è un componente di base, chiamato Text, che a sua volta restituisce un oggetto conforme al tipo some View richiesto dal body. Questa sintassi fa parte di un concetto nuovo introdotto con Swift 5.1, chiamato Opaque Types. Per ora basta sapere che l'opaque type richiesto dal body ci permette di scambiare oggetti di cui non conosciamo in anticipo il tipo specifico, ma che è ben determinato a livello di compilazione (a differenza di un semplice protocol).
Opaque types
Restituire un opaque type sembra molto simile a usare un protocol, ma i due costrutti differiscono perché l'opaque type conserva il tipo. Un opaque type si riferisce a un tipo specifico, anche se chi chiama la funzione non è in grado di vedere quale sia. Un protocol, invece, può riferirsi a qualunque tipo che lo adotti: offre quindi più flessibilità sul tipo restituito, mentre gli opaque type ci danno garanzie precise sul tipo restituito.
Facciamo un esempio per chiarire meglio il concetto:
mport UIKit
protocol Humanoid {
}
struct Woman : Humanoid {
}
struct Man : Humanoid {
}
func child() -> Humanoid {
Bool.random() ? Woman() : Man()
}
Il protocol Humanoid è adottato da due struct: Man e Woman. La funzione child restituisce un Humanoid che può essere, con uguale probabilità, uno dei due casi. A questo punto creiamo due istanze di Humanoid:
let first = child()
let second = child()
e, siccome siamo curiosi di sapere se i nostri figli sono maschi o femmine, proviamo a confrontarli:
print((first == second))
A questo punto il compilatore ci rimprovererà, giustamente, perché Humanoid non è confrontabile: non adotta il protocol Equatable. Niente male, penserete. Proviamo ad adottarlo:

Il problema è che Humanoid non può essere confrontato perché Swift non ha alcuna garanzia che gli elementi restituiti siano di un tipo confrontabile. Ricordo che qualunque struct può adottare il protocol, implementando i metodi richiesti.
Per risolvere questo problema sono nati gli opaque type. Il codice precedente può essere trasformato così:
import UIKit
protocol Humanoid : Equatable {
}
struct Woman : Humanoid {
}
struct Man : Humanoid {
}
func boy() -> some Humanoid {
Man()
}
func girl() -> some Humanoid {
Woman()
}
let primogenito = boy()
let secondogenito = girl()
Un'osservazione importante sul codice qui sopra: come potete vedere, ho dovuto creare due funzioni diverse (boy per il maschio, girl per la femmina), perché uno dei requisiti principali quando si lavora con un opaque type è che i metodi devono restituire un solo tipo di oggetto. Ritroverete questo concetto molto spesso in SwiftUI quando lavoreremo con le View.
