Specificità di Swift nello sviluppo mobile: cosa sono, caratteristiche chiave e come funzionano

Autore: IT Sectr Pubblicato: 2026-06-25 Tempo di lettura: 17 min

Swift è il linguaggio di programmazione di Apple che combina la potenza di Objective-C con la sicurezza dei linguaggi moderni. A differenza di molti altri linguaggi, Swift offre costruzioni uniche: i tipi valore con copia automatica, gli Actor con isolamento a livello di compilatore, i Property Wrappers per riutilizzare la logica delle proprietà e la Programmazione Orientata ai Protocolli come alternativa all'ereditarietà classica. Secondo la Documentazione Apple Swift, queste caratteristiche rendono il codice più sicuro e performante.

Punti Chiave

  • Struct — tipo valore, copiato nell'assegnazione, non supporta l'ereditarietà. Class — tipo riferimento, passato per riferimento, supporta ereditarietà e deinit.
  • Actor — tipo riferimento thread-safe con isolamento a livello di compilatore. Il compilatore impedisce l'accesso concorrente allo stato mutabile da diverse attività.
  • Property Wrappers — @Published, @State, @Binding incapsulano la logica delle proprietà in SwiftUI e Combine, eliminando il codice boilerplate.
  • Generics permettono di scrivere funzioni e strutture type-safe senza vincolarsi a un tipo specifico. La Programmazione Orientata ai Protocolli usa extension + protocol invece della gerarchia di classi.
  • Enum con valori associati — un potente strumento per modellare gli stati. Ogni caso può memorizzare un insieme arbitrario di dati di diversi tipi.

Struct vs Class in Swift: tipi valore e tipi riferimento

In Swift, struct è un tipo valore, mentre class è un tipo riferimento. Questa differenza fondamentale determina il comportamento durante l'assegnazione, il passaggio alle funzioni e la gestione della memoria. Struct viene copiato interamente a ogni assegnazione, class viene passato per riferimento — viene copiato solo il puntatore all'oggetto nell'heap.

Semantica di Valore di Struct

Struct implementa automaticamente la semantica di valore — ogni istanza è indipendente. La mutazione di una proprietà di struct è possibile solo tramite var, e i metodi che modificano le proprietà devono essere contrassegnati come mutating. Struct non supporta l'ereditarietà ma può conformarsi ai protocolli. La libreria standard di Swift usa struct per String, Array, Dictionary, Int, Bool — tutti i tipi fondamentali sono tipi valore.

swift
struct User {
    let id: Int
    var name: String

    mutating func updateName(_ newName: String) {
        name = newName
    }
}

var user1 = User(id: 1, name: "Alice")
var user2 = user1
user2.updateName("Bob")
// user1.name === "Alice", user2.name === "Bob"
// Copia completa — istanze indipendenti

Semantica di Riferimento di Class

Class viene memorizzato nell'heap e supporta l'uguaglianza per riferimento. Più variabili possono riferirsi allo stesso oggetto, le modifiche tramite un riferimento sono visibili attraverso tutti gli altri. Class supporta l'ereditarietà, permette l'override dei metodi (override), ha deinit per la pulizia delle risorse. In Swift, class è usata meno frequentemente di struct — Apple raccomanda di iniziare con struct e passare a class solo quando sono necessarie ereditarietà o semantica di riferimento.

swift
class Car {
    let model: String
    var speed: Int

    init(model: String, speed: Int) {
        self.model = model
        self.speed = speed
    }

    func accelerate(_ amount: Int) {
        speed += amount
    }
}

let car1 = Car(model: "Tesla", speed: 0)
let car2 = car1
car2.accelerate(50)
// car1.speed === 50, car2.speed === 50
// Stesso riferimento — modifiche visibili ovunque

La scelta tra struct e class dipende dalla semantica dei dati. Per configurazioni, modelli dati e stati usa struct — la copia è più sicura e prevedibile. Per oggetti con identità (sessione utente, connessione database) e quando serve ereditarietà, usa class.

Actor — sicurezza dei thread in Swift

Actor è un tipo riferimento che protegge il suo stato mutabile dall'accesso concorrente. Il compilatore Swift garantisce che le variabili di actor siano accessibili solo dallo stesso contesto dell'actor — qualsiasi accesso esterno causa un errore di compilazione. Questo previene le race condition a livello di linguaggio, senza lock manuali.

Isolamento di Actor

Tutte le proprietà e i metodi di actor sono isolati per impostazione predefinita. Chiamare un metodo di actor dall'esterno richiede await — Swift commuta l'esecuzione all'actor e viceversa. L'actor sincronizza l'accesso sequenzialmente: le richieste da diverse attività vengono messe in coda. Questo garantisce uno stato coerente all'interno dell'actor. I metodi non isolati (nonisolated) possono essere chiamati senza await ma non possono accedere alle proprietà mutabili.

swift
actor BankAccount {
    private var balance: Double

    init(initialBalance: Double) {
        balance = initialBalance
    }

    func deposit(amount: Double) {
        balance += amount
    }

    func withdraw(amount: Double) throws {
        guard balance >= amount else {
            throw BankError.insufficientFunds
        }
        balance -= amount
    }

    nonisolated func accountInfo() -> String {
        return "Conto Bancario"
    }
}

let account = BankAccount(initialBalance: 1000)
await account.deposit(amount: 500)
// balance = 1500 — atomico, senza race condition

Actor è utile per gestori di stato, cache e servizi di rete dove più attività leggono e scrivono dati concorrentemente. A differenza della sincronizzazione manuale tramite NSLock o DispatchQueue, l'actor viene verificato in fase di compilazione — è impossibile sbagliare. Gli errori di isolamento appaiono in fase di compilazione, non in fase di esecuzione.

Property Wrappers: @Published, @State, @Binding

Property Wrapper è un'annotazione che aggiunge comportamento a una proprietà senza modificarne la dichiarazione. SwiftUI e Combine forniscono wrapper integrati per la gestione dello stato delle viste. Il compilatore genera codice che incapsula l'accesso alla proprietà, aggiungendo logica di osservazione, sincronizzazione o trasformazione.

@Published e @State

@Published viene usato nelle classi che conformano a ObservableObject per pubblicare automaticamente i cambiamenti. Gli abbonati Combine ricevono notifiche a ogni aggiornamento. @State è lo stato locale di una vista SwiftUI. SwiftUI ridisegna automaticamente la vista quando @State cambia. @Binding crea una connessione bidirezionale tra una vista padre e figlia senza possedere i dati.

swift
import SwiftUI
import Combine

@main
struct CounterApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class CounterViewModel: ObservableObject {
    @Published var count: Int = 0

    func increment() {
        count += 1
    }
}

struct ContentView: View {
    @State private var greeting: String = "Ciao"
    @StateObject private var viewModel = CounterViewModel()

    var body: some View {
        VStack {
            CounterDisplay(count: $viewModel.count)
            Text(greeting)
            Button("Incrementa", action: viewModel.increment)
        }
    }
}

struct CounterDisplay: View {
    @Binding var count: Int

    var body: some View {
        Text("Conteggio: \(count)")
    }
}

I Property Wrappers personalizzati vengono creati con @propertyWrapper e una struttura con un campo wrappedValue. Questo permette di riutilizzare la logica: validazione, normalizzazione, archiviazione UserDefaults. I Property Wrappers riducono significativamente il codice boilerplate e rendono esplicita l'intenzione dello sviluppatore tramite annotazione.

swift
@propertyWrapper
struct Clamped<T: Comparable> {
    private var value: T
    let range: ClosedRange<T>

    init(wrappedValue: T, range: ClosedRange<T>) {
        self.range = range
        self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
    }

    var wrappedValue: T {
        get { value }
        set { value = min(max(newValue, range.lowerBound), range.upperBound) }
    }
}

struct Settings {
    @Clamped(range: 0...100) var volume: Int = 50
}

var s = Settings()
s.volume = 150
// s.volume === 100 — valore automaticamente limitato

Generics e Programmazione Orientata ai Protocolli

Generics è un potente meccanismo di Swift per scrivere codice flessibile senza perdere la sicurezza dei tipi. Funzioni e tipi generici funzionano con qualsiasi tipo che soddisfi i vincoli. La Programmazione Orientata ai Protocolli (POP) è un paradigma Swift dove protocol + extension sostituiscono l'ereditarietà classica delle classi, fornendo composizione di comportamento.

Vincoli Generici e Tipi Associati

Swift permette di vincolare i parametri generici con protocolli (clausole where). Protocollo con tipo associato è l'equivalente generico per i protocolli: il tipo concreto viene determinato al momento della conformità. Questa è la base per collezioni e algoritmi type-safe. La libreria standard di Swift usa attivamente i generics: Array, Dictionary, Optional — sono tutti tipi generici.

swift
protocol Cacheable {
    associatedtype Key: Hashable
    associatedtype Value

    func get(_ key: Key) -> Value?
    mutating func set(_ value: Value, for key: Key)
}

struct MemoryCache<K: Hashable, V>: Cacheable {
    private var storage: [K: V] = [:]

    typealias Key = K
    typealias Value = V

    func get(_ key: K) -> V? {
        return storage[key]
    }

    mutating func set(_ value: V, for key: K) {
        storage[key] = value
    }
}

func firstMatch<T: Equatable>(in array: [T], target: T) -> Int? {
    for (index, item) in array.enumerated() {
        if item == target {
            return index
        }
    }
    return nil
}

let numbers = [10, 20, 30, 40]
let index = firstMatch(in: numbers, target: 30)
// index === 2 — un metodo generico per tutti i tipi Equatable

Extension di Protocollo e Implementazioni Predefinite

L'extension di protocollo è un meccanismo chiave di POP. In un'estensione si può fornire un'implementazione predefinita dei metodi del protocollo. I tipi che conformano al protocollo ottengono questa implementazione gratuitamente e possono sovrascriverla se necessario. Questo permette di costruire gerarchie di comportamento senza ereditarietà di classi — un'alternativa all'ereditarietà multipla, che non esiste in Swift.

swift
protocol Drivable {
    var speed: Double { get set }
    func drive()
}

extension Drivable {
    func drive() {
        if speed > 0 {
            print("Guidando a \(speed) km/h")
        }
    }
}

struct Bicycle: Drivable {
    var speed: Double = 15
}

struct Plane: Drivable {
    var speed: Double = 900

    func drive() {
        print("Volando a \(speed) km/h")
    }
}

let bike = Bicycle()
bike.drive() // "Guidando a 15.0 km/h" — predefinita

let plane = Plane()
plane.drive() // "Volando a 900.0 km/h" — sovrascritta

POP con extension permette la composizione attraverso molteplici protocolli — un tipo può conformarsi a diversi protocolli, ottenendo le loro implementazioni. Questo è più flessibile dell'ereditarietà singola delle classi, dove l'intera gerarchia è fissa. La libreria standard di Swift è costruita su POP: CustomStringConvertible, Equatable, Hashable, Codable — sono tutti protocolli con estensioni.

Enum con valori associati ed Extension

Enum in Swift è una costruzione significativamente più potente che in altri linguaggi. Ogni caso può avere valori associati — un insieme arbitrario di dati di qualsiasi tipo. Combinato con extension e pattern matching, enum diventa la base per modellare stati, risultati di operazioni e macchine a stati finiti.

Valori Associati e Pattern Matching

I valori associati permettono a ogni caso di memorizzare dati unici. Ad esempio, il caso loading non contiene dati, success contiene il risultato, failure contiene un errore. Il pattern matching tramite switch estrae i valori associati e gestisce ogni caso. Il compilatore verifica l'esaurività (exhaustive switch) — questo elimina gli stati dimenticati.

swift
enum NetworkResult<T> {
    case loading
    case success(T)
    case failure(Error)
}

enum MediaFile {
    case image(url: URL, width: Int, height: Int)
    case video(url: URL, duration: Double)
    case audio(url: URL, bitrate: Int)
}

func handle(_ result: NetworkResult<String>) {
    switch result {
    case .loading:
        print("Caricamento...")
    case .success(let data):
        print("Ricevuto: \(data)")
    case .failure(let error):
        print("Errore: \(error.localizedDescription)")
    }
}

let file = MediaFile.video(url: URL(string: "https://example.com/video.mp4")!, duration: 120)

if case .video(let url, let duration) = file {
    print("Video \(url) durata \(duration)s")
}

Extension per Enum

Swift permette di aggiungere metodi, proprietà calcolate e conformità ai protocolli tramite extension per enum. Questo non richiede di modificare la dichiarazione originale — il codice rimane pulito e modulare. I metodi nell'estensione possono lavorare con valori associati, calcolare dati derivati e implementare logica di business.

swift
extension MediaFile {
    var fileName: String {
        switch self {
        case .image(let url, _, _),
             .video(let url, _),
             .audio(let url, _):
            return url.lastPathComponent
        }
    }

    var description: String {
        switch self {
        case .image(_, let w, let h):
            return "Immagine \(w)x\(h)"
        case .video(_, let d):
            return "Video \(d)s"
        case .audio(_, let b):
            return "Audio \(b) kbps"
        }
    }
}

extension MediaFile: Equatable {
    static func == (lhs: MediaFile, rhs: MediaFile) -> Bool {
        return lhs.fileName == rhs.fileName
    }
}

let files: [MediaFile] = [file]
files.forEach { print($0.description) }
// "Video 120.0s"

Enum con valori associati + extension è il sostituto delle classi sealed di Kotlin o delle unioni discriminate di altri linguaggi. Swift usa questa combinazione per Result (un tipo standard nella libreria standard), Optional (Optional è un enum con .none e .some) e la gestione degli stati in SwiftUI. Il pattern matching garantisce che tutti gli stati siano gestiti — gli errori a runtime sono impossibili.

Domande Frequenti

In cosa differisce struct da class in Swift?

Struct — tipo valore, copiato nell'assegnazione, non supporta ereditarietà. Class — tipo riferimento, passato per riferimento, supporta ereditarietà, deinit e controllo d'identità (===). Apple raccomanda di iniziare con struct e passare a class solo quando è necessaria la semantica di riferimento.

Cos'è Actor in Swift e quando usarlo?

Actor — tipo riferimento thread-safe. Il compilatore impedisce l'accesso concorrente allo stato mutabile da diversi contesti. Usa Actor per gestori di stato, cache e servizi accessibili da più attività. Chiamare metodi di actor richiede await.

Quali Property Wrappers vengono usati in SwiftUI?

Principali: @State — stato locale della vista; @Binding — connessione ai dati senza possesso; @Published — pubblicazione delle modifiche in ObservableObject; @StateObject — possesso di ObservableObject; @EnvironmentObject — dipendenza dall'ambiente. I wrapper personalizzati si creano tramite @propertyWrapper.

Cos'è la Programmazione Orientata ai Protocolli?

POP — un paradigma dove protocol + extension definiscono il comportamento invece dell'ereditarietà di classe. Un tipo si conforma a più protocolli, ottenendo le loro implementazioni gratuitamente. È composizione invece di ereditarietà — più flessibile e sicuro. La libreria standard di Swift (Equatable, Codable) è costruita su POP.

In cosa differisce enum in Swift da enum in altri linguaggi?

Enum in Swift supporta valori associati — ogni caso può memorizzare dati arbitrari di tipi diversi. Combinato con il pattern matching (switch), fornisce un potente modello per stati, errori e Domain-Driven Design. Optional e Result sono enum standard di Swift con valori associati.

Riepilogo

  • Struct vs Class — differenza fondamentale: i tipi valore vengono copiati, i tipi riferimento vengono passati per riferimento. Struct non supporta ereditarietà, class sì. Apple raccomanda struct per impostazione predefinita.
  • Actor — sicurezza dei thread a livello di compilatore. Stato mutabile isolato, accesso tramite await, protezione dalle race condition senza lock manuali.
  • Property Wrappers — @Published, @State, @Binding e personalizzati tramite @propertyWrapper. Riducono il codice boilerplate e rendono esplicita l'intenzione tramite annotazione.
  • Generics — funzioni e tipi type-safe con vincoli where. Protocol + tipo associato per astrazioni. La libreria standard di Swift è interamente costruita sui generics.
  • Programmazione Orientata ai Protocolli — composizione di comportamento tramite extension di protocollo. Le implementazioni predefinite permettono il riutilizzo del codice senza ereditarietà di classi.
  • Enum con valori associati — un potente modello per gli stati. Ogni caso memorizza dati unici. Il pattern matching garantisce che tutti gli stati siano gestiti.
  • Extension — aggiunge metodi, proprietà calcolate e conformità ai protocolli per qualsiasi tipo, incluso enum. Il codice rimane modulare e pulito senza modificare la dichiarazione originale.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto