Perché Swift lancia il messaggio ‘Trovato inaspettatamente nil durante l’unwrapping di un valore opzionale’ quando si usa SwiftUI su iOS?

Il messaggio di errore è abbastanza auto esplicativo. Il runtime si aspettava di non trovare nil durante l'unwrapping di una variabile opzionale che hai creato, ma ne ha trovato uno lì, invalidando così tutto il codice che viene dopo quella linea, quindi l'app è andata in crash.

In forma ancora più prolissa: Avete fatto una promessa al compilatore e poi l'avete infranta. Avete detto al compilatore che eravate certi che la vostra variabile sarebbe stata non-nil al momento dell'accesso, e poi il compilatore vi ha dimostrato di essere un gran bugiardo. Avete fatto questa promessa usando l'operatore you-should-practically-never-use-this, il "bang", che assomiglia a questo: !

L'operatore bang è usato per denotare un opzionale esplicitamente unwrapped, o un opzionale force-unwrapped. Cioè, è una variabile di tipo opzionale (cioè può essere o non essere nil) per la quale voi, come programmatore, state facendo una promessa al compilatore che non sarà nil nel momento in cui vi si accede.

Questo avviene o al momento della dichiarazione della variabile, come questo:

  1. var myNecessaryInt: Int! 

o al momento dell'accesso, come questo:

  1. var myNecessaryInt: Int? 
  2. let myNecessaryString = "(myNecessaryInt!)" 

Nel secondo caso, è meno come rompere una promessa e più come dire all'ultimo minuto, "Fidati di me! So che è lì! Ti mentirei mai?"

Questo errore si incontra solo quando qualcuno ha pensato che usare if let o guard let fosse un'enorme seccatura o un'egregia perdita di tempo. Piuttosto che usare una semplice costruzione che renderebbe il loro codice sicuro, hanno scelto di assumere che il loro normale cervello umano possa facilmente pensare ad ogni possibile scenario e punto nel tempo in cui la variabile potrebbe essere accessibile. The solution: trust the tools of the language and write safe code rather than writing less code.

In both examples above, simply architecting the file so that the method accessing the optional could either exit early (if it’s a method that returns Void):

  1. var myNecessaryInt: Int? 
  2.  
  3. func doSomeStuffWithMyInt() { 
  4. guard let necessaryInt = myNecessaryInt else { 
  5. return 
  6. let theStringVersion = “(necessaryInt)” 
  7. // and so on... 

or that throws an error in the case that the variable is nil:

  1. var myNecessaryInt: Int? 
  2.  
  3. func doSomeStuffWithMyInt() throws { 
  4. guard let necessaryInt = myNecessaryInt else { 
  5. throw NSError(domain: "com.myApp",  
  6. code: 0,  
  7. userInfo: ["description": "The number wasn't there."]) 
  8. let theStringVersion = “(necessaryInt)” 
  9. // and so on... 

A prescindere da come lo gestite, se state scrivendo codice Swift stabile e sicuro, allora il 99,999% delle volte eviterete di usare !

Per quanto riguarda il motivo per cui va in crash su quell'errore con SwiftUI, l'unica cosa che posso pensare è che un opzionale esplicitamente non avvolto sia usato da qualche parte nel vostro file SwiftUI. Forse l'oggetto del modello che lo sostiene è un opzionale "force-unwrapped", o uno viene alimentato nella variabile di stato che è legata alla vista? In entrambi i casi, quando l'errore viene lanciato, dovrebbe catturare il nome del file e il numero di linea, e se si aggiunge un punto di interruzione dell'eccezione in Xcode, dovrebbe anche fermarsi su quella linea e permettervi di ispezionare le variabili disponibili nell'ambito della linea incriminata.