---
title: "Programmare in SwiftUI: introduzione agli stati"
url: https://www.sofapps.it/it/blog/swiftui-programming-states/
date: 2020-01-09
author: Costantino Pistagna
language: it
translated_from: en
tags: [SwiftUI, Tutorial]
---

# Programmare in SwiftUI: introduzione agli stati

> In SwiftUI una view è una struct immutabile e @State permette al framework di allocare una variabile fuori da essa e ridisegnare il body ogni volta che il valore cambia. Si evitano così lo stato sparso e i problemi di threading di un view controller UIKit. Il post estrae anche una subview, le passa parametri e un modello, e mostra che @State non reagisce ai cambiamenti interni a una class.

Abbiamo visto in [un articolo precedente](https://www.sofapps.it/it/blog/swiftui-programming-classes-structs/) che in SwiftUI una View è una struct che *adotta* il *protocol View*. Quindi non eredita alcuna stored property, semplicemente perché non ha nessun padre; viene allocata sullo stack e gestita per valore e non per riferimento (come accade invece con le class in UIKit).

## Come funzionano le view in SwiftUI

Dietro le quinte, **SwiftUI** gestisce le View con l'aiuto di una struttura dati molto efficiente per il rendering a schermo dei contenuti. Per questo motivo è consigliabile usare e creare componenti **specializzati** e **riutilizzabili**. Durante lo sviluppo, Cupertino ci rassicura, non dobbiamo esitare a estrarre le view che svolgono un compito specifico e a creare per loro un oggetto View dedicato.

<hr>

>Insieme al body, una View in SwiftUI definisce le sue dipendenze.

<hr>

Supponiamo di voler creare in SwiftUI una View specializzata nel creare e gestire un pulsante. Il nostro oggetto dovrà occuparsi di un solo compito: alla pressione, la label dovrà cambiare riflettendo il nuovo stato.

Ed è proprio qui che la programmazione dichiarativa dà il meglio: ***la conseguenza di un'azione come modo per aggiornare l'interfaccia***.

## L'attributo @State in SwiftUI

Il nostro pulsante avrà quindi una dipendenza tra il suo stato e la sua rappresentazione a schermo. In SwiftUI questa dipendenza è gestita automaticamente dal nuovo modifier @State. Proviamo con un esempio per capire meglio il concetto.

```swift

struct MyButton : View {
    @State private var toggleStatus:Bool = false
    var body: some View {
        Button(action: {
            self.toggleStatus = !self.toggleStatus
        }) {
            Text((toggleStatus == false) ? "Press Me" : "Unpress me")
        }
    }
}

```

**@State** lega la variabile toggleStatus all'interfaccia: ogni volta che il suo valore cambia, SwiftUI sa che è necessario generare un nuovo body (e quindi aggiornare la label associata).

![Fig.1: Semplice schema che illustra il flusso dei dati del codice precedente.](https://www.sofapps.it/blog/swiftui-programming-states/swiftuiviewdataflow.jpg)

### Provate a rimuovere @State dal codice precedente. Cosa succede e perché?

>Rimuovendo l'attributo @State, il compilatore si lamenterà perché stiamo cercando di modificare un valore immutabile: non dimentichiamo che abbiamo a che fare con una struct.
>Ogni oggetto che viene renderizzato a schermo, infatti, è immutabile per ragioni di performance. In questo modo SwiftUI sa cosa deve essere aggiornato e cosa può essere lasciato fuori.
>Aggiungendo un modifier @State, stiamo dicendo esplicitamente a SwiftUI:
>
>"Attenzione! Qui c'è un oggetto che potrebbe cambiare il suo stato, quindi dovrai aggiornarne il contenuto."

Di conseguenza, SwiftUI definisce implicitamente una dipendenza tra questa variabile e la presentazione a schermo della View ogni volta che incontra un attributo @State. In questo modo, ogni volta che la variabile cambia valore, SwiftUI sa che la View deve essere ridisegnata con il nuovo valore.

Per fissare questo concetto e vedere quanto sia diverso dalla programmazione tradizionale, facciamo un confronto con UIKit:

```swift

import UIKit

class ViewController: UIViewController {
	@IBOulet var aButton: UIButton!
	var toggleStatus:Bool = false
	
	override func viewDidLoad() {
		super.viewDidLoad()
	}

	@IBAction func buttonDidPressed(sender: UIButton) {
		sender.setTitle((toggleStatus) ? "Unpress me" : "Press me", for: .normal)
		toggleStatus = !toggleStatus
	}
}

```

Naturalmente in **UIKit** il codice qui sopra non basta per ottenere una schermata funzionante. Dovremo creare uno **Storyboard** con un UIViewController e un UIButton. Dovremo inoltre allineare il pulsante al centro con i **Constraints** e creare i riferimenti **@IBOutlet** e **@IBAction** corretti per far funzionare l'intero flusso.

Lasciando da parte la quantità di codice e di operazioni, ovviamente maggiore rispetto a SwiftUI, concentriamoci sul nostro **stato**. Anche in questo caso, infatti, il nostro pulsante deve conoscere lo stato corrente per poter cambiare la sua rappresentazione (la label). Notate come, nella programmazione imperativa tradizionale, ci siano due problemi concettuali importanti:

- **toggleStatus** è gestito (allocato, inizializzato e modificato) dal Controller.
- Non esiste un modo chiaro, a livello di codice, per separare le variabili che riguardano lo stato dell'interfaccia da quelle tradizionali: sono tutte uguali!

![Fig.2: Uno schema che illustra le relazioni e il flusso del nostro oggetto Button in UIKit.](https://www.sofapps.it/blog/swiftui-programming-states/viewcontrollerstatusdataflow.jpg)

È anche evidente come la complessità di questo approccio cresca in modo esponenziale all'aumentare del numero di oggetti e di stati.

![Fig.3: Schema che illustra l'aumento della complessità al crescere degli stati gestiti dall'interfaccia.](https://www.sofapps.it/blog/swiftui-programming-states/interfacecomplexityvsstate-en.jpg)

C'è ancora una cosa da sottolineare: iOS è un sistema operativo ***multithreaded***. Questo significa che le richieste di aggiornamento dello stato e dell'interfaccia possono arrivare da più punti del codice e che i thread lavorano in modo indipendente l'uno dall'altro. Senza un modo chiaro per gestire situazioni di questo tipo si arriva a incoerenze nella UI e quindi a bug difficili da scoprire e da correggere.

![Fig.4: In un'applicazione multithreaded il confine tra chi aggiorna l'interfaccia e chi aggiorna lo stato non è chiaro.](https://www.sofapps.it/blog/swiftui-programming-states/uiviewcontrollermultithreaddataflow.jpg)

## La ricerca della verità
Ma come viene gestita in SwiftUI questa dipendenza tra variabili e interfaccia? E cosa rende il sistema resiliente e scalabile, a differenza di un approccio imperativo tradizionale?

Quando SwiftUI elabora una View con una variabile di tipo @State, alloca in memoria uno spazio per questa variabile per conto della View.

![Fig.5: Allocazione di una variabile @State in SwiftUI.](https://www.sofapps.it/blog/swiftui-programming-states/stateallocationinswiftuien.jpg)

Aggiungendo l'attributo **@State** possiamo modificare un oggetto che sarebbe ***altrimenti immutabile***, come una struct: dietro le quinte, infatti, la variabile viene allocata dal framework SwiftUI in un'area di lettura/scrittura dell'applicazione.

In SwiftUI ogni possibile stato di una View (l'offset di una scrollview, lo stato di un pulsante, il contenuto di uno stack e così via) è gestito da questo concetto, noto come **Source of Truth**. Seguendo questo concetto è possibile classificare ogni variabile come ***source of truth*** oppure come ***valore derivato***.

![Fig.6: Schema che mostra la classificazione delle variabili in SwiftUI.](https://www.sofapps.it/blog/swiftui-programming-states/sourceoftruthtableen.jpg)

Nel nostro esempio, **toggleStatus** è una **source of truth**. Inoltre, dallo schema qui sopra possiamo dedurre che anche tutte le **costanti** sono una source of truth, mentre una semplice property è un valore derivato.

Vedremo nei prossimi capitoli che esiste un altro meccanismo per passare valori derivati in lettura/scrittura, chiamato **@Binding**. Con un esempio pratico vedremo anche in cosa un valore derivato differisce dalla corrispondente source of truth. Analizzeremo le differenze con **@State** e come e quando usare l'uno rispetto all'altro.

## Organizzare il codice: l'estrazione delle view

Ora che abbiamo chiaro come una View si aggiorna e ricostruisce il suo contenuto, proviamo a mettere insieme quello che abbiamo imparato con l'esempio dell'ultimo articolo. L'ultima volta avevamo lasciato la nostra ContentView così:

```swift

import SwiftUI

struct ContentView : View {
    var body: some View {
        VStack {
            HStack {
                Image("photoThumb")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .clipShape(Circle())
                    .frame(width: 60, height: 60, alignment: .center)
                    .padding(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 0))
                VStack(alignment: .leading) {
                    Text("Mario Rossi").font(.title)
                    Text("Amministratore delegato").font(.subheadline).fontWeight(.thin)
                }
                Spacer()
            }
        }
        
    }
}

```

Chiaramente questo approccio non è mantenibile. Il codice tende ad annidarsi e a crescere verso destra, nella cosiddetta <a href="https://itnext.io/pyramid-of-doom-the-signs-and-symptoms-of-a-common-anti-pattern-c716838e1819"> "pyramid of doom"</a>.

All'inizio dell'articolo abbiamo accennato a un meccanismo per estrarre le subview, così da rendere il codice scalabile e mantenibile. Vediamo cosa ci offrono il nuovo Xcode 11 e SwiftUI per risolvere questo problema.

Assicuratevi di usare Xcode 11 e portate il mouse sul tag HStack. Tenendo premuto il tasto CMD (⌘), cliccate sul tag, come segue:

![Fig.7: Xcode crea automaticamente una nuova struct e ci dà la possibilità di cambiarne il nome.](https://www.sofapps.it/blog/swiftui-programming-states/extractsubviewstep1.jpg) ![Fig.7: Xcode crea automaticamente una nuova struct e ci dà la possibilità di cambiarne il nome.](https://www.sofapps.it/blog/swiftui-programming-states/extractsubviewstep2.jpg)

Xcode crea automaticamente una nuova struct, estrae il contenuto selezionato e lo usa per popolare il body dell'oggetto appena generato. Alla fine dell'operazione ci offre anche la possibilità di cambiare il nome: rinominate la View estratta in **CustomTableViewCell**.

La nuova versione di **ContentView** è ora molto più leggibile e leggera, e contiene solo un riferimento a un nuovo oggetto. Come ulteriore comodità, potremmo anche spostare il contenuto di **CustomTableViewCell** in un nuovo file, per isolare meglio il concetto di componente.

## Passare parametri

Non sarebbe interessante passare i parametri giusti alla nostra nuova CustomTableViewCell, per generare una cella con contenuti dinamici come titolo, sottotitolo e immagine? Proviamo a modificare la nostra nuova struct per ottenere questo risultato:

```swift

struct CustomTableCell : View {
    var photoName:String
    var title:String
    var subtitle:String
    
    var body: some View {
        return HStack {
            Image(photoName)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .clipShape(Circle())
                .frame(width: 60, height: 60, alignment: .center)
                .padding(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 0))
            VStack(alignment: .leading) {
                Text(title).font(.title)
                Text(subtitle).font(.subheadline).fontWeight(.thin)
            }
            Spacer()
        }
    }
}

```

Ora, dentro la **ContentView**, la nostra **CustomTableCell** può essere creata con parametri che ne definiscono in modo univoco il contenuto.

```swift

struct ContentView : View {
    var body: some View {
        VStack {
            CustomTableCell(photoName: "photoThumb", 
                                title: "Gianluca Verdi", 
                             subtitle: "Consigliere")
        }
        
    }
}

```

<hr>

>E se volessimo spingerci oltre e creare, per esempio, un pulsante che cambia il contenuto del campo subtitle?

<hr>

## Il modello, l'oggetto e SwiftUI

Il modello collegato a un oggetto View ha un ruolo fondamentale nel paradigma dichiarativo di SwiftUI. Ogni oggetto ha tipicamente un modello associato che, insieme allo stato, identifica completamente l'interfaccia utente mostrata.

Per esempio, potremmo associare alla nostra **CustomTableViewCell** un modello di questo tipo:

```swift

struct Person {
    var photoName:String
    var title:String
    var subtitle:String
}

```

Di conseguenza dovremo modificare la nostra View per sfruttare il nuovo modello appena creato:

```swift

struct CustomTableViewCell : View {
    var model: Person
    
    var body: some View {
        return HStack {
            Image(model.photoName)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .clipShape(Circle())
                .frame(width: 60, height: 60, alignment: .center)
                .padding(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 0))
            VStack(alignment: .leading) {
                Text(model.title).font(.title)
                Text(model.subtitle).font(.subheadline).fontWeight(.thin)
            }
            Spacer()
            }
    }
}

```

Infine, la **ContentView** deve riflettere questa modifica per mostrare correttamente a schermo la nostra **CustomTableViewCell**.

```swift

struct ContentView : View {
    @State private var model = Person(photoName: "photoThumb", 
                               title: "Mario Rossi", 
                            subtitle: "Amministratore delegato")
    
    var body: some View {
        VStack {
            CustomTableViewCell(model: model)
            Button(action: {
                self.model.subtitle = "Consigliere"
            }) {
                Text("Change subtitle")
                .foregroundColor(Color.white)
                .padding(EdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8))
                .background(Color.blue)
                .cornerRadius(8)
            }
        }
    }
}

```

Questa volta la variabile model ha l'attributo **@State**: il proprietario del modello è l'oggetto **ContentView**, che lo passa per copia (***ricordiamo che stiamo lavorando con struct e non con class***) alla **CustomTableViewCell**, la quale a sua volta lo usa per mostrare i campi corrispondenti dei suoi oggetti: Image e Text.

Il pulsante ha un'azione che aggiorna il modello cambiandone il subtitle. Poiché ContentView ha aggiunto l'attributo **@State** al suo modello, ogni modifica di quest'ultimo fa scattare l'aggiornamento del suo body che, a sua volta, *rigenera* una nuova CustomTableViewCell con il contenuto aggiornato.

![Fig.8: Schema di flusso che illustra l'aggiornamento dello stato dell'oggetto.](https://www.sofapps.it/blog/swiftui-programming-states/dataflowpassingargs.jpg)

Avrete notato che ho aggiunto l'attributo **private** prima di dichiarare la variabile di stato del modello. Questo perché, come abbiamo spiegato sopra, le variabili marcate con **@State** appartengono esclusivamente alla View che le dichiara. È quindi una buona idea rafforzare questo concetto marcandole come private.

Vedremo nel prossimo articolo che **SwiftUI** espone altri modi per gestire i parametri e l'injection quando ci troviamo in situazioni come quella illustrata sopra. In questo caso, infatti, pur essendo corretto dal punto di vista sintattico e funzionale, il nostro flusso soffre di un piccolo problema: la nostra CustomTableViewCell usa una propria copia del modello, che a sua volta è esposto, allocato e gestito da ContentView con una variabile privata.

SwiftUI offre astrazioni molto simili all'attributo @State per gestire queste circostanze e permettere al modello di propagarsi correttamente dove serve.

### Cosa succede se cambiamo il modello da struct a class?

>Anche se il progetto continuerà a compilare e a girare senza problemi, purtroppo noteremo che in pratica il pulsante non cambierà più il contenuto del campo subtitle.
>
>@State funziona con i value type, mentre le class sono reference type: cambiando il valore di una class marcata con l'attributo @State, la sua modifica interna non fa scattare l'aggiornamento del body.

-- 🖖🏻
