Home Testo Corso Swift Gestione Errori

Gestione Errori

617
1
Gestione-Errori

In questa lezione vediamo come gestire gli errori che potrebbero verificarsi in una applicazione infatti è matematico, hanno condotto studi a riguardo, più aumenta il numero di righe del codice, più aumentano gli errori che il codice potrebbe generare.

Ma, cos’è un errore?

Non pensare subito agli errori che potrebbero verificarsi da un’errata scrittura del codice. Per quelli come abbiamo visto ci pensa già Xcode a segnalarceli, Gli errori, che vedrai in questa lezione sono quelli di tipo logico.

Per errore logico si intende quel tipo d’errore che, pur rispettando la semantica e i vincoli del codice, genera dei risultati inaspettati che potrebbero compromettere il funzionamento dell’applicazione.

L’esempio potrebbe essere sempre il nostro programma per la gestione di conto corrente.

Tutti sappiamo che in un conto bancario il saldo iniziale non può, certamente, essere minore di zero. Però, se il deposito bancario viene rappresentato da un valore di tipo Double, l’applicazione è comunque predisposta ad accettare valori negativi.

Il problema potrebbe essere risolto come abbiamo visto in diversi esempi con utilizzo delle istruzioni if else, con l’istruzione guard o con i modificatori d’accesso.

l problema, se si può chiamare tale, è che le istruzioni di controllo, fin ora utilizzate, pur svolgendo egregiamente questo lavoro, non sono adatte per gestire errori in sistemi più complessi come quelli che vedrai nel corso IOS, Infatti il miglior modo è quello di gestire tali errori a livello d’applicazione e non semplicemente in quello specifico istante.

Ti sarà capitato che, provando ad inserire un valore errato in un campo di testo, l’applicazione risponda con un opportuno messaggio: “Non è possibile utilizzare caratteri speciali , o ancora: “Non puoi utilizzare frasi razziste“.

Sicuramente ti sarà anche capitato che l’applicazione venisse chiusa a seguito di qualche operazioni non consentita, o un numero talmente alto di operazione, da mandare in tilt l’applicazione.

Ma come fa l’applicazione a rispondere diversamente ad ogni tipo d’errore? Come sceglie di comportarsi in modo diverso in base al tipo d’errore?

É proprio questo il punto! Per questo si parla di gestione degli errori in Swift.

Gestire significa poter controllare. Il controllo ti permette di far defluire correttamente l’utilizzatore nella giusta direzione anche quando ha commesso degli errori.

Per poter gestire gli errori, correttamente e in piena libertà, sono necessari degli strumenti realizzati ad hoc.

Ecco perché Apple ha deciso di adottare il sistema dell’Error Handling, o gestione degli errori, dalla versione 2.0 del linguaggio Swift. 

Con il nuovo sistema, la gestione degli errori in Swift (o eccezioni con il linguaggio Swift), si potrà personalizzare gli errori e preformare le azioni che ti permetteranno di controllare la propagazione dell’errore.

In informatica e per i linguaggi di programmazione come Swift, il termine errore è sinonimo di eccezione.

Una eccezione è una condizione o evento che altera il normale flusso d’esecuzione del codice.

Purtroppo il linguaggio Swift non è in grado di capire a priori quando un codice potrebbe generare un errore di tipo logico o che potrebbe mandare in crash l’applicazione.

Per questo motivo, anche il codice ha bisogno delle tue cure e attenzioni.

Come si dice: Prevenire è meglio che curare, no?

La prevenzione parte da delle semplici domande:

  1. Cos’è che potrebbe generare un errore in questo codice?
  2. Ho individuato tutti i possibili fattori d’errore?
  3. Ci sono errori che non dipendono dalla natura del codice?

Partendo da questi presupposti, una volta che hai individuato le eccezioni, queste vanno catalogate.

let contatti = ["Francesco", "Maria", "Carlo"]

// 01
func inviaMessaggio(destinatario: String, messaggio: String) {
    
    print("messaggio: \(messaggio) ")
    print("inviato a: \(destinatario) ")
    print("---------------------------")

}

inviaMessaggio(destinatario: "Tizio", messaggio: "Come Stai? Tutto bene!")

Ho creato una semplice funzione per l’invio di un messaggio, analizzando la natura logica del codice, quali problemi si potrebbero verificarsi?

  1. Il destinatario del messaggio non è presente nella lista dei contatti.
  2. Non è stato inserito un destinatario.
  3. Non è stato inserito il messaggio.
  4. altri…

Una volta individuati, come cataloghiamo gli errori con il linguaggio Swift?

Un errore per il linguaggio Swift è rappresentato dal protocollo Error.

Quindi un’eccezione è un oggetto conforme al protocollo Error (introdotto con la versione di Swift 2.0 come ErrorType e da swift 3.0 come solo Error).

Per catalogare le possibili eccezioni si utilizza un enum conforme al protocollo Error:

enum tipoErrore: Error{

    case destinatarioNonPresente
    case messaggioNonPresente
    case destinatarioNonInserito

}

A questo punto come facciamo a gestirli all’interno del codice?

Fino ad ora hai visto gli esercizi tramite l’utilizzo del codice sul Playground.

Il Playground ti da la possibilità di testare il codice sia dal punto di vista dello sviluppatore che dal punto di vista dell’utilizzatore. Mi spiego meglio.

Quando realizzeremo la nostra prima applicazione, nel prossimo corso, ti accorgerai che l’unico sistema che ha l’utente per poter interagire con l’applicazione sono i bottoni, i campi di testo, immagini ecc ecc. Questi a loro volta sono collegati con dei metodi e funzioni di classi create ad hoc.

In questo modo l’utente interagirà con la logica dell’applicazione solo ed esclusivamente grazie alle funzioni e metodi da te creati.

In base a questa considerazione, che per adesso devi prendere per buona, i metodi e le funzioni sono i primi generatori d’errore di una qualsiasi applicazione.

Il linguaggio Swift ti permette di marcare un metodo, o funzione, che potrebbe generare delle eccezioni.

Il marcatore ha un compito ben preciso, avvisa l’applicazione e/o il compilatore che lì potrebbero nascere dei problemi: “Fai attenzione quando esegui questo codice, potrebbe generare degli errori”.

Questo sistema permette di circoscrivere le aree possibili d’errore. Non è detto che succederà, ma se lo farà avrai uno strumento che lo controllerà.

Per marcare una funzione o metodo, come generatore di un probabile errore, si utilizza la parola chiave throws.

La parola chiave throws va inserita subito dopo la definizione della funzione e precisamente dopo la scrittura dei parametri d’ingresso. Se la funzione ha dei tipi di ritorno, il throws in Swift, va inserito prima della freccia (->).

func inviaMessaggio(destinatario: String, messaggio: String) throws {
    
    print("messaggio: \(messaggio) ")
    print("inviato a: \(destinatario) ")
    print("---------------------------")
    
}

Nel mio esempio potremmo scrivere così

func inviaMessaggio2(destinatario: String, messaggio: String) throws {
    
    guard destinatario != ""  else {
        throw tipoErrore.destinatarioNonInserito
    }
    
    guard messaggio != ""  else {
        throw tipoErrore.messaggioNonPresente
    }
        
    var esistenteDestinatario = false
    
    for contatto in contatti {
        
        if contatto == destinatario  {
        esistenteDestinatario = true
        }
    }
    
    guard esistenteDestinatario == true else {
        throw tipoErrore.destinatarioNonPresente
    }
    
    print("messaggio: \(messaggio) ")
    print("inviato a: \(destinatario) ")
    print("---------------------------")
    
}

Sollevare un’eccezione con il linguaggio Swift significa avvisare l’applicazione della nascita d’un errore o eccezione.

Ora, immagina che, all’interno di una funzione, nasca un errore. Diremo che la funzione ha sollevato un’eccezione quando comunicherà la comparsa di un problema e delegherà all’esterno la gestione ed il trattamento di quella particolare eccezione.

Nel caso dell’invio del messaggio, solleveremo un’eccezione quando tramite un controllo non verrà rispettata una delle clausole per l’invio del testo. Ad esempio, quando il destinatario non è presente nella lista solleveremo l’eccezione ErroreInvioMessaggio.destinatarioNonPresente.

Per sollevare un’eccezione in Swift, devi utilizzare la parola chiave throw seguita dal tipo d’errore.

Quando l‘istruzione guard viene attivata, quindi quando entra nel blocco else, viene anche sollevata l’eccezione ErroreInvioMessaggio e più precisamente destinatarioNonInserito.

La throw possiamo immaginarla alla stregua dell’istruzione return.

Se il return esce tranquillamente dalla funzione, la throw esce bruscamente sollevando un’eccezione.

Oltre ad uscire dalla funzione o metodo, porta con se anche il motivo dell’uscita ovvero il tipo d’eccezione sollevata.

Cosa succede se proviamo a chiamare la funzione throws?

il Playground ci comunica che non puoi effettuare una chiamata classica in quanto la funzione potrebbe essere soggetta a delle eccezioni. Dato che la funzione throws potrebbe sollevare delle eccezioni, quest’ultime da qualcuno andranno pur controllate.

Per controllare la chiamata ad una funzione throws, essa va ingabbia all’interno di un recinto delineato dall’istruzione do-catch. Inoltre la chiamata all’istruzione throws va preceduta dalla keyword try

do {

print("Sto Provando a inviare il messaggio")
    print("-------------------------------")
    try inviaMessaggio2(destinatario: "Francesco", messaggio: "Ciao")
}
catch{
    
    print(" è stata sollevata un'eccesione \(error)")
}

L’istruzione do-catch del linguaggio Swift puoi leggerla così: Esegui il codice (do) e nel caso di un errore acchiappalo (catch) e gestiscilo”.

Il try, all’interno del blocco do, ti dice che il compilatore proverà (try) ad eseguire la funzione. Se la funzione solleverà un’eccezione, l’esecuzione del blocco do verrà interrotta ed il catch provvederà a gestirla.

Nell’esempio ho utilizzato un solo catch che notifica il tipo di errore generico ma posso anche personalizzarlo inserendo uno per ogni tipo di errore come di seguito

do {
    
    print("Sto Provando a inviare il messaggio")
    print("-------------------------------")
    try inviaMessaggio2(destinatario: "Francesco", messaggio: "")
}
catch tipoErrore.destinatarioNonPresente {
    print("Il destinatario non è presente nella lista Contatti")
}
catch tipoErrore.destinatarioNonInserito {
    print("Il destinatario non è stato Specificato.")
}
catch tipoErrore.messaggioNonPresente{
    print("Inserisci un messaggio")
}

In questo modo il compilato ci notifica che si è verifica un errore e ci indica anche il tipo di errore ma la potenza di questo sistema è la possibilità di gestire ogni singole errore e di comportarsi diversamente a secondo del tipo di errore.

Per qualunque dubbio o domanda scrivi un commento in questa lezione.

1 commento

  1. Ciao,
    e creare una funzione per la gestione di tutti gli errori dell’applicazione?
    Una cosa tipo:

    enum tipoErrore: Error{
    case destinatarioNonPresente
    case messaggioNonPresente
    case destinatarioNonInserito
    }
    func gestioneErrori (codiceErrore: Error) {
    switch codiceErrore {
    case tipoErrore.destinatarioNonPresente:
    print(“Il destinatario non è presente nella lista Contatti”)
    case tipoErrore.destinatarioNonInserito:
    print(“Il destinatario non è stato Specificato.”)
    case tipoErrore.messaggioNonPresente:
    print(“Inserisci un messaggio”)
    default:
    print(“Errore non identificato”)
    }
    }

    //—————————————–
    // Oppure richiamare una funzione di gestione degli errori
    do {
    print(“Sto Provando a inviare il messaggio”)
    print(“——————————-“)
    try inviaMessaggio2(destinatario: “” , messaggio: “Ciao”)
    }
    catch{
    gestioneErrori(codiceErrore: error)
    }