Non-Fatal Error — è un errore che non termina l’applicazione e consente di proseguire l’esecuzione del programma. A differenza di un fatal error, gli errori non fatali possono essere intercettati, gestiti e registrati senza perdere la sessione dell’utente. Secondo la Documentazione di Firebase Crashlytics, 2024, circa il 70% di tutti gli errori registrati nelle applicazioni di produzione sono non fatali, ma ignorarli porta all’accumulo di debito tecnico e al graduale degrado dell’esperienza utente. La corretta gestione degli errori non fatali è una delle competenze chiave dello sviluppatore mobile.
Punti chiave
Non-Fatal Error — è un’eccezione o uno stato di errore che non causa la terminazione del processo. L’applicazione continua a funzionare, ma può trovarsi in uno stato non corretto: dati non caricati, richiesta non inviata, elemento dell’interfaccia non visualizzato. L’utente o non nota l’errore, o vede un messaggio e continua a utilizzare l’applicazione.
Un errore non fatale lascia sempre al programma una via di recupero. Il gestore degli errori può fornire dati alternativi, ripetere l’operazione o mostrare un placeholder dell’interfaccia. L’obiettivo principale è prevenire un crash e mantenere un’esperienza utente accettabile. Lo sviluppatore deve prevedere esplicitamente uno scenario di recupero in ogni blocco catch.
Secondo Instabug 2024, il 65% degli utenti disinstalla un’app dopo due interazioni fallite. Gli errori non fatali lasciati senza attenzione si accumulano e degradano la qualità complessiva. La registrazione e la correzione sistematica degli errori non fatali è un percorso diretto per migliorare la fidelizzazione e le valutazioni negli store delle applicazioni.
Errori di rete sono il tipo più comune di errori non fatali nelle applicazioni mobili. Timeout di connessione, perdita di rete, codice di stato del server errato — tutte queste situazioni vengono intercettate e gestite senza crash. All’utente viene mostrato un messaggio di indisponibilità del servizio con opzione di riprovare. Il pattern di ripetizione con backoff esponenziale è tipico per gli errori di rete.
Formato di risposta del server errato, campo obbligatorio mancante, tipo di dato non valido — gli errori di parsing sono non fatali se l’applicazione gestisce correttamente i dati malformati. L’approccio tipico è utilizzare valori predefiniti di fallback e registrare l’errore di parsing con il contesto della richiesta per un’analisi successiva lato server.
Problemi di caricamento delle immagini, caratteri errati, errori di layout — tutti sono non fatali ma degradano l’esperienza utente. Le immagini placeholder e i valori di fallback aiutano a evitare schermate vuote e rendono gli errori meno evidenti. In React Native, si utilizza Error Boundary per gli errori dell’interfaccia con visualizzazione di un componente di fallback.
Errori di calcolo, incongruenze di stato, transizioni errate tra schermate — gli errori logici spesso non causano un crash ma portano a un comportamento errato dell’applicazione. Sono più difficili da rilevare senza registrazione e monitoraggio sistematici poiché non generano un report di crash e rimangono inosservati fino a un reclamo dell’utente.
Non-Fatal Error si differenzia dal fatale in quanto lascia al programma la possibilità di continuare a funzionare. Un fatal error è uno stato da cui l’applicazione non può riprendersi: dereferenziazione di puntatore nullo, overflow dello stack, esaurimento della memoria. Un errore non fatale può essere intercettato, gestito e l’esecuzione può continuare, mentre un fatal error richiede il riavvio dell’applicazione.
| Caratteristica | Non-Fatal Error | Fatal Error |
|---|---|---|
| Terminazione dell’app | No | Sì |
| Recupero possibile | Sì, tramite catch | No |
| Registrazione | Dal codice tramite recordException | Solo dal crash reporter |
| Impatto UX | Disagio temporaneo | Fallimento completo della sessione |
| Esempio | Timeout di rete, errore di parsing | NullPointerException, OOM |
Il confine tra non fatale e fatale può dipendere dall’implementazione. Un timeout di rete in un’applicazione viene gestito come non fatale (riprova dopo 1–2 secondi), mentre in un’altra può essere fatale (crash se non c’è un gestore). Una gestione degli errori di qualità trasforma situazioni potenzialmente fatali in non fatali, aumentando la stabilità dell’applicazione. Progettare un sistema di gestione degli errori è uno dei compiti architetturali chiave nello sviluppo di un’applicazione mobile con elevati requisiti di affidabilità. Un sistema di monitoraggio integrato consente al team di rilevare e correggere rapidamente gli errori non fatali prima che influiscano su un numero significativo di utenti.
Firebase Crashlytics è lo strumento principale per registrare gli errori non fatali nelle applicazioni mobili. Il metodo recordException consente di acquisire un’eccezione non fatale con stack trace completo e contesto di esecuzione senza interrompere l’applicazione. A differenza dei report di crash, recordException può essere chiamato in qualsiasi punto del codice per registrare le eccezioni intercettate.
fun fetchUserData(userId: String) {
try {
val response = apiService.getUser(userId)
updateUI(response)
} catch (e: IOException) {
Crashlytics.log("Network error for user $userId")
Crashlytics.recordException(e)
showRetryDialog()
} catch (e: JsonParseException) {
// Non-fatal: utilizzo dati di fallback
Crashlytics.recordException(e)
showFallbackContent()
}
}
// Registrazione con chiavi personalizzate
Crashlytics.setCustomKey("screen", "Profile")
Crashlytics.setCustomKey("api_version", "v3")
Sentry è un’alternativa a Crashlytics con diagnostica più dettagliata per gli errori non fatali. L’SDK di Sentry fornisce il metodo captureException, che invia i dettagli dell’eccezione al server. Il vantaggio principale di Sentry è raggruppare errori non fatali simili in un unico issue, analizzare la frequenza di ricorrenza e fornire il contesto di esecuzione come breadcrumbs — la sequenza delle azioni dell’utente prima dell’errore.
Non tutti gli errori non fatali devono essere registrati. Gli stati previsti — errore di rete in assenza di connessione — possono essere registrati selettivamente. Gli errori imprevisti — NullPointerException in codice gestito, formato dati non valido, errori logici — devono essere sempre registrati. Ogni team definisce la propria soglia di importanza: in media, da 10 a 20 errori non fatali unici per 1000 utenti al giorno è considerato normale. È importante impostare avvisi per un aumento improvviso degli errori non fatali — ciò può indicare problemi con una nuova versione dell’API o una regressione dopo un rilascio.
Il meccanismo di base è try-catch, che intercetta l’eccezione ed esegue il codice di recupero. Per le operazioni di rete, il pattern tipico è il tentativo con backoff esponenziale. Per gli errori di parsing, l’approccio è utilizzare valori predefiniti di fallback e registrare il contesto per un’analisi successiva lato server.
func loadImage(from url: URL) -> UIImage? {
do {
let data = try Data(contentsOf: url)
return UIImage(data: data)
} catch {
Logger.shared.logError(error: "Image load failed: \(url)")
return UIImage(named: "placeholder")
}
}
func performRequest() async throws -> Data {
var lastError: Error? = nil
for attempt in 0..<3 {
do {
return try await URLSession.shared.data(from: url)
} catch {
lastError = error
try await Task.sleep(UInt64(pow(2, attempt)) * 1_000_000_000)
}
}
throw lastError ?? URLError(.unknown)
}
Tipi Result — un approccio alternativo senza eccezioni. Una funzione restituisce una classe sealed Result con le varianti Success e Failure. Il codice chiamante gestisce esplicitamente entrambe le varianti, eliminando errori non gestiti. I tipi Result sono popolari in Kotlin (Result
Per ogni tipo di errore non fatale, dovrebbe essere pianificata una strategia di recupero: caricare i dati nella cache in caso di errore di rete, utilizzare valori predefiniti in caso di errore di parsing, reinizializzare un componente in caso di errore dell’interfaccia. Una buona pratica è mostrare all’utente un toast o snackbar con un messaggio di errore senza bloccare completamente l’interazione con l’applicazione. È importante distinguere tra errori recuperabili e non recuperabili — per questi ultimi, la strategia di recupero sarà diversa, come suggerire di riavviare la schermata o cancellare i dati. La memorizzazione nella cache dello stato precedente riuscito è spesso il modo più semplice ed efficace per gestire gli errori non fatali sulle piattaforme mobili.
Domande frequenti
Warning è un avviso del compilatore o analizzatore statico su un potenziale problema nel codice. Un errore non fatale è un’eccezione runtime che si è già verificata ma non ha causato un crash. Un warning può essere risolto prima della compilazione; un errore non fatale deve essere gestito durante l’esecuzione tramite un blocco catch.
No, una registrazione eccessiva appesantisce il monitoraggio. Gli errori imprevisti in produzione dovrebbero essere registrati, mentre gli stati previsti dovrebbero essere ignorati: l’errore di rete quando si è offline può essere registrato selettivamente, ma un NullPointerException in codice gestito deve essere sempre registrato. Ogni team definisce la propria soglia di importanza in base al contesto dell’applicazione.
In SwiftUI, si utilizza ObservableObject con un campo @Published errorState per tracciare lo stato di errore. La View si iscrive alle modifiche e mostra contenuto alternativo. Prima di iOS 17, si utilizzava Combine con gestori; a partire da iOS 17, si utilizzano SwiftData e le macro @Observable per aggiornamenti reattivi dell’interfaccia.
Sì, se l’errore innesca una reazione a catena. Esempio: un fallimento non fatale nel caricamento di un’immagine può portare a uno stato dell’interfaccia errato, che poi causa un crash quando si tenta di visualizzarla. Una gestione di qualità degli errori non fatali a ogni livello impedisce la loro escalation a livello fatale.
Su iOS, gli errori non fatali vengono gestiti tramite do-catch con throw; su Android, tramite try-catch con eccezioni. iOS utilizza NSError con domini e codici di errore; Android utilizza eccezioni Java/Kotlin. Crashlytics funziona in modo identico su entrambe le piattaforme tramite recordException, fornendo un’interfaccia di monitoraggio unificata.
Riepilogo
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.
Leggi anche