Do-Catch: cos'è, costruzione e gestione degli errori in Swift

Autore: IT Sectr Pubblicato: 2026-05-26 Tempo di lettura: 8 min

Do-Catch è una costruzione del linguaggio Swift per la gestione degli errori che cattura le eccezioni lanciate tramite throw ed esegue il blocco di codice corrispondente. A differenza del tradizionale try-catch in altri linguaggi, Swift richiede la marcatura esplicita delle funzioni che possono lanciare un errore con il modificatore throws. Secondo Swift.org, 2026, Do-Catch è il meccanismo principale di gestione degli errori in Swift, che fornisce sicurezza dei tipi.

Punti chiave

  • Do-Catch è una costruzione Swift che cattura errori da blocchi con try e throw.
  • throws è un marcatore di funzione che indica che può lanciare un errore.
  • try è la parola chiave per chiamare una funzione throwing all'interno di un blocco do.
  • catch è il blocco di gestione degli errori con corrispondenza di pattern per tipo.
  • try? e try! sono forme alternative che convertono errori in nil o causano un crash.

Cos'è Do-Catch?

Do-Catch è una costruzione in Swift composta da un blocco do, all'interno del quale viene eseguito codice in grado di lanciare un errore, e uno o più blocchi catch che gestiscono tale errore. Il blocco do contiene chiamate a funzioni throwing marcate con try, e i blocchi catch corrispondono all'errore per tipo.

Swift implementa un modello di gestione strutturata degli errori, diverso dalle eccezioni in Objective-C. Qui un errore non è un'eccezione con svolgimento dello stack, ma un valore conforme al protocollo Error. Lanciare un errore tramite throw trasferisce il controllo al blocco catch più vicino senza l'overhead dello svolgimento dello stack.

Una caratteristica fondamentale di Swift è la gestione esplicita. Il compilatore non consente di chiamare una funzione throwing senza try, do-catch o un meccanismo alternativo (try?, try!). Ciò elimina situazioni in cui un errore passa inosservato.

Modello di errori in Swift: protocollo Error e funzioni throwing

Al centro della gestione degli errori in Swift c'è il protocollo Error. Qualsiasi tipo conforme a questo protocollo può essere lanciato tramite throw. Gli errori sono tipicamente definiti come un enum conforme a Error.

Protocollo Error

Error è un protocollo vuoto (marcatore). Non richiede implementazione di metodi, solo conformità. Il compilatore Swift usa Error per il controllo statico: una funzione con throws può lanciare solo un valore conforme a Error.

Funzioni Throwing

Una funzione marcata con throws deve essere chiamata con try. Se si verifica un throw all'interno di tale funzione, il controllo viene trasferito al do-catch chiamante. Se l'errore non viene gestito in nessun punto della catena di chiamate, si propaga al livello superiore.

swift
enum FileError: Error {
    case notFound
    case permissionDenied
    case corrupted(String)
}

func readFile(path: String) throws -> String {
    guard FileManager.default.fileExists(atPath: path) else {
        throw FileError.notFound
    }
    return try String(contentsOfFile: path)
}

Rethrows

rethrows è un modificatore per funzioni che accettano una closure throwing come parametro. Se la closure passata non lancia un errore, la funzione rethrows può essere chiamata senza try. Ciò consente di scrivere funzioni generiche di ordine superiore compatibili sia con closure throwing che non throwing.

Sintassi di Do-Catch: blocchi catch multipli

Swift supporta multipli blocchi catch con corrispondenza degli errori per tipo. Ciò è simile a when in Kotlin o uno switch per tipo: il primo catch corrispondente viene eseguito, gli altri vengono saltati.

Corrispondenza per tipo

Ogni blocco catch può contenere un pattern che corrisponde a un tipo specifico di errore. Swift controlla i blocchi in ordine, quindi i tipi più specifici devono venire prima di quelli generali. Se nessun pattern corrisponde, viene eseguito il blocco catch senza pattern.

Gestione di tipi multipli

La sicurezza dei tipi di Do-Catch consente di gestire ogni errore separatamente o combinare più tipi in un blocco tramite corrispondenza di pattern separata da virgole. Ciò elimina le catene if-else ingombranti tipiche di Objective-C, dove l'analisi degli errori veniva effettuata tramite codici di ritorno o NSError con controllo di dominio e codice.

Swift supporta anche catch where — corrispondenza con una condizione aggiuntiva. Ad esempio, puoi filtrare gli errori per un codice o testo specifico: catch URLError where error.code == .notConnectedToInternet. Ciò riduce il numero di blocchi catch mantenendo la precisione.

Per il debug delle funzioni throwing, Swift fornisce una distinzione tra errori ed eccezioni. Un errore Swift è un valore di ritorno che non richiede svolgimento dello stack. Le eccezioni Objective-C (@try/@catch) funzionano a livello di runtime e sono usate solo per guasti fatali. Do-Catch gestisce errori, non eccezioni, rendendolo prevedibile ed efficiente.

Quando si sviluppano librerie e SDK, è importante ricordare che le funzioni throwing fanno parte del contratto pubblico. Il tipo di errore non è specificato nella firma, quindi documenta quali errori una funzione può lanciare nei commenti o tramite enum simili a Result. Ciò aiuta gli utenti dell'API a scrivere blocchi catch corretti senza dover guardare nell'implementazione.

Nel contesto della programmazione asincrona, Do-Catch con async/await risolve il problema del callback hell. In precedenza in Swift, la gestione degli errori nel codice asincrono richiedeva closure annidati con controllo di Error? nei completion handler. Con async/await e Do-Catch, il codice asincrono throwing appare come codice sincrono, semplificando la lettura e la manutenzione di catene di richieste complesse con gestione degli errori in ogni passaggio.

swift
do {
    let content = try readFile(path: "/data/config.json")
    process(content)
} catch FileError.notFound {
    createDefaultConfig()
} catch FileError.permissionDenied {
    requestAccess()
} catch FileError.corrupted(let detail) {
    logCorruption(detail)
} catch {
    print("Errore sconosciuto: \(error)")
}

Variazioni di try: try, try?, try! e quando usarli

Swift offre tre modi per chiamare le funzioni throwing: try, try? e try!. Ogni opzione risolve un compito specifico e ha i suoi limiti.

try — Chiamata standard

try viene usato all'interno di un blocco do e l'errore viene gestito in catch. Questo è il modo principale per chiamare le funzioni throwing. Il compilatore richiede che try sia in un contesto in cui l'errore può essere catturato.

try? — Conversione in Optional

try? converte il risultato di una funzione throwing in un Optional. Se la funzione lancia un errore, try? restituisce nil, altrimenti restituisce un Optional con il valore riuscito. Utile per chiamate in cui l'errore può essere ignorato ma è necessario un indicatore di successo.

try! — Esecuzione forzata

try! sopprime la gestione degli errori. Se la funzione throwing lancia un errore, l'app termina con un errore di runtime. Usa try! solo quando sei assolutamente sicuro che l'errore sia impossibile — ad esempio, durante il caricamento di una risorsa incorporata.

swift
// try? — ignoriamo l'errore, otteniamo nil in caso di fallimento
if let data = try? Data(contentsOf: url) {
    processData(data)
}

// try! — solo se l'errore è garantitamente impossibile
let bundled = try! String(contentsOfFile: "Assets/default.txt")

// try — opzione standard in do-catch
do {
    let result = try performNetworkRequest()
    updateUI(result)
} catch {
    showError(error)
}

Do-Catch nello sviluppo iOS: esempi pratici

Do-Catch è attivamente usato nello sviluppo iOS per lavorare con le API di sistema. Consideriamo scenari tipici: lavoro con il file system, Core Data e richieste di rete.

File system

Molti metodi di FileManager sono throwing. Do-Catch consente di gestire correttamente l'assenza di file, l'occupazione di risorse o permessi insufficienti.

Richieste di rete con Codable

L'analisi JSON tramite JSONDecoder è un'operazione throwing. Do-Catch cattura errori di decodifica e guasti di rete separatamente, fornendo all'utente un messaggio di errore preciso.

swift
struct User: Codable {
    let id: Int
    let name: String
}

func fetchUser(id: Int) async throws -> User {
    let url = URL(string: "https://api.example.com/users/\(id)")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(User.self, from: data)
}

// Chiamata con gestione
do {
    let user = try await fetchUser(id: 42)
    showUser(user)
} catch let error as URLError {
    showNetworkAlert(error)
} catch let error as DecodingError {
    showParseError(error)
} catch {
    showGenericError(error)
}

Errori comuni con Do-Catch

Gli sviluppatori, specialmente quelli che provengono da altri linguaggi, commettono spesso errori caratteristici nell'uso di Do-Catch. Esaminiamo i più comuni.

  • Try dimenticato — chiamare una funzione throwing senza try causa un errore di compilazione. Swift non consente di ignorare implicitamente gli errori.
  • Catch vuoto — un blocco catch senza gestione nasconde l'errore. Registra o gestisci sempre l'errore, anche se sembra impossibile.
  • Catch senza corrispondenza — un singolo catch generico per tutti gli errori perde i vantaggi della sicurezza dei tipi. Separa la gestione per tipi usando blocchi multipli.
  • Uso eccessivo di try! — try! nel codice di produzione è una fonte di crash inaspettati. Usalo solo per risorse incorporate o test.

Domande frequenti

In che modo Do-Catch in Swift differisce dal try-catch in altri linguaggi?

In Swift, un errore è un valore conforme al protocollo Error, non un'eccezione con svolgimento dello stack. Le funzioni throwing devono essere esplicitamente marcate con throws, e il compilatore richiede try alla chiamata — ciò elimina errori non gestiti al momento della compilazione.

Si può usare Do-Catch con async/await?

Sì, le funzioni async possono anche essere throws e do-catch funziona con esse. Chiamare una funzione async throwing richiede try await all'interno di un blocco do. Questo è il modo standard per gestire gli errori nel codice asincrono Swift.

Come gestire un errore senza do-catch?

Ci sono alternative: try? converte l'errore in nil, try! causa un crash in caso di errore e rethrows consente di propagare un errore da una closure. Do-catch rimane il modo principale di gestione esplicita.

Una funzione può lanciare diversi tipi di errori?

Swift non tipizza gli errori lanciati — qualsiasi funzione throwing può lanciare qualsiasi tipo conforme a Error. Separa la gestione tramite corrispondenza in catch per tipi specifici di errore.

Dovrei incapsulare tutti gli errori in un tipo personalizzato?

Sì, è una buona pratica. Definisci AppError come un enum conforme a Error e converti gli errori di sistema in errori di dominio. Ciò unifica la gestione e isola il livello applicativo dai dettagli di sistema.

Riepilogo

  • Do-Catch è una costruzione Swift per la gestione degli errori con marcatura obbligatoria throws sulle funzioni e try sulle chiamate.
  • Gli errori Swift sono valori conformi al protocollo Error, non eccezioni con svolgimento dello stack, offrendo overhead zero in assenza di guasto.
  • I catch multipli consentono di gestire diversi tipi di errore separatamente con corrispondenza di pattern simile a switch.
  • try? converte un errore in nil per scenari semplici, try! è una chiamata forzata con rischio di crash, usata solo per operazioni garantite.
  • Do-Catch con async/await è il modo standard per gestire errori nel codice asincrono Swift usando try await.
  • Evita i blocchi catch vuoti e l'uso eccessivo di try! — ciò nasconde errori e porta a crash inaspettati in produzione.

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

Leggi anche