Fatal error: Unexpectedly found nil while unwrapping an Optional value

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Fatal error: Unexpectedly found nil while unwrapping an Optional value

    Hallo,

    ich stehe gerade auf dem Schlauch und verstehe hier was nicht. Ich speichere Zugangsdaten in der Keychain, welche ich bei bedarf auch dort wieder raus hole. Allerdings ist seid kurzem hier ein Problem aufgetreten, das ich nicht verstehe:

    Quellcode

    1. let username:String = keychain.get(keychainKeys.username) ?? ""
    2. let password:String = keychain.get(keychainKeys.password) ?? ""
    3. let serverurl:String = keychain.get(keychainKeys.url) ?? ""
    4. webdavList = WebDAVFileProvider(baseURL: URL(string: serverurl)!, credential: credential)
    an der stelle, an der ich webdavList eine URL übergeben soll: "URL(string: serverurl)!" Crash nun die App und meint "Unexpectedly found nil while unwrapping an Optional value". Wenn ich das ganze Schritt für Schritt durchgehe, dann ist in serverurl die Url zu finden

    Bildschirmfoto 2020-06-15 um 09.49.38.png

    aber eine Ausgabe in der Console von

    Quellcode

    1. po URL(string: serverurl)!
    bringt dann ein:


    Quellcode

    1. Fatal error: Unexpectedly found nil while unwrapping an Optional value: file <EXPR>, line 7
    2. error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
    3. The process has been returned to the state before expression evaluation.
    Und das verstehe ich nicht. Warum ist das nil?

    Auch wenn ich vorher eine variable deklariere z.B.

    Quellcode

    1. let url = URL(string: serverurl)!
    dann ist in der variablen "url" nil drin.

    Bildschirmfoto 2020-06-15 um 09.53.05.png
    Bildschirmfoto 2020-06-15 um 09.53.19.png

    Was mache ich denn plötzlich falsch?
  • Das ! sollte man nur dann verwenden, wenn man als Programmierer sicherstellen kann, dass das nicht schief geht. Soll heißen, wenn es schief geht, dann ist es ein Programmierfehler, der nur durch den Programmierer behoben werden kann.

    In deinem Fall scheint es ja so zu sein, dass der Anwender des Programms die URL angeben kann, d.h. du als Programmierer kannst nicht sicherstellen, dass die URL korrekt eingegeben wird. Hier ein ! zu verwenden ist also der falsche Weg. Wenn in der Keychain keine Server-URL steht, wird auch dein „Workaround“-Code immer noch abstürzen.
  • Hallo Michael,

    danke für deine Nachricht. Ich weiß das ! ein force Unwrap ist. Leider fordert der Compiler diese force Unwraps. Deswegen haben ich die variablen mit einem optional value ausgestattet, das wenn diese eigentlich nil enthalten würden dort ein "" drin steht. Danach schlägt einfach die Prüfung der Verbindung fehl und teilt dies dem Anwender auch mit.

    Da ich aber nicht wirklich der Vollprofi Programmierer bin, frage ich mich halt auch immer, ob ich dem was der Compiler da vom mir verlangt auch nachgeben muss, oder ob es andere Möglichkeiten gibt. Wäre cool zu erfahren wie man das "richtig" macht. Danke im voraus.

    Gruß

    Dirk
  • Ich habe ähnlichen Code bei mir.

    Quellcode

    1. guard let username = getUsername() else { throw Fehler.noUsername }
    Der Fehler wird ein der aufrufenden Funktion gefangen und die Einstellungen angezeigt. Geht es gut brauchst du kein ! mehr.
    URLs setze ich mit URLComponents zusammen. hauptsächlich weil ich teilweise Fallbackserver habe.

    Du hast in deinem Code var files:[String] = [String()]
    Damit legst du ein Array mit einem Leerstring an. Wie reagiert dein Code darauf?
    Man macht einfach solange irgendwelche Dinge, bis man tot ist.
    Und dann bekommen die anderen Kuchen.
  • Qvex23 schrieb:

    ...
    Leider fordert der Compiler diese force Unwraps. Deswegen haben ich die variablen mit einem optional value ausgestattet, das wenn diese eigentlich nil enthalten würden dort ein "" drin steht. Danach schlägt einfach die Prüfung der Verbindung fehl und teilt dies dem Anwender auch mit.

    Da ich aber nicht wirklich der Vollprofi Programmierer bin, frage ich mich halt auch immer, ob ich dem was der Compiler da vom mir verlangt auch nachgeben muss, oder ob es andere Möglichkeiten gibt. Wäre cool zu erfahren wie man das "richtig" macht.
    ...
    Hallo,

    anbei ein Link der Dir bestimmt helfen wird: Otionals

    Nicht alles was der Compiler vorschlägt bzw. nicht vorschlägt ist sinnig. Nur Du bist im Bilde welches Ergebnis erzielt bzw. zu welchem Zeitpunkt die genutzten Variablen welche Ergebnisse aufweisen sollten. Wenn Du Dir nicht sicher bist, greife auf den Debugger zurück. Dieser ist ein adäquates Hilfsmittel, welcher Dir hilfreiche Informationen zur Verfügung stellt.
    Bedenke - Aller Anfang ist schwer, deshalb nicht aufgeben. Je mehr Du liest und probierst, desto größer wird Deine Wissensbasis und je einfacher wird die nächste Aufgabe.
  • Hallo,

    danke für alle Hilfen. @Chris wie soll mein Coda darauf reagieren? Ich habe an der stelle ein leere Arrays von Strings, welches dann gefüllt wird!

    Ich habe da noch mal eine Frage zu Optionals:

    Quellcode

    1. let url = URL(string: serverurl.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)!
    Diese Zeile führt immer mal zum Absturz, ich vermute wenn die vom User eingegebene URL irgendwie falsch ist. Ich würde das gerne abfangen, so das die App nicht terminiert. Was könnte ich hier tun, damit die URL bei einem Fehler einfach leer ist?
  • Qvex23 schrieb:

    ...

    Quellcode

    1. let url = URL(string: serverurl.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)!
    Diese Zeile führt immer mal zum Absturz, ich vermute wenn die vom User eingegebene URL irgendwie falsch ist. Ich würde das gerne abfangen, so das die App nicht terminiert. Was könnte ich hier tun, damit die URL bei einem Fehler einfach leer ist?

    Chris, hat Dir die Lösung für derartige Fragestellungen bereits mitgeteilt. Lese Dir mal in aller Ruhe die Beschreibung für guard() durch.
  • Es ist so leider schwer zu beurteilen, wo das Problem liegt. Ich tippe darauf, dass die Codezeile falsch ist
    und der Fehler wie oben beim Erstellen der URL auftritt.

    Ich versuche mal den Code, der hier immer nur in Auszügen zu sehen ist, umzuschreiben:

    Quellcode

    1. let username:String = keychain.get(keychainKeys.username) ?? ""
    2. let password:String = keychain.get(keychainKeys.password) ?? ""
    3. if let serverurl:String = (keychain.get(keychainKeys.url) ?? "").addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) {
    4. if let url = URL(string: serverurl) {
    5. let credential = URLCredential(user: username, password: password, persistence: .none)
    6. if let webdavList = WebDAVFileProvider(baseURL: url, credential: credential) {
    7. // ...
    8. }
    9. }
    10. }

    alternativ kann man auch die vorgeschlagenen guards nehmen:

    Quellcode

    1. let username:String = keychain.get(keychainKeys.username) ?? ""
    2. let password:String = keychain.get(keychainKeys.password) ?? ""
    3. guard let serverurl:String = (keychain.get(keychainKeys.url)?.).addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
    4. else { throw Fehler.invalidUrl }
    5. guard let url = URL(string: serverurl)
    6. else { throw Fehler.invalidUrl }
    7. let credential = URLCredential(user: username, password: password, persistence: .none)
    8. guard let webdavList = WebDAVFileProvider(baseURL: url, credential: credential)
    9. else { throw Fehler.noWabDav }
    10. // ...
    Alles anzeigen
    In beiden Fällen ist es leichter zu erkennen, dass der mit '// ...' angedeutete Code nur in bestimmten Fällen
    überhaupt ausgeführt wird.

    Die zweite Variante (mit guard) bietet zudem eine Fehlererkennung. Der Aufrufer kann die Ursachen anhand
    der Fehler unterscheiden und protokollieren. Das kann man bei der ersten Variante natürlich noch ergänzen.
    Die einfachste Form zur Fehlersuche wäre die Ausgabe von Fehlermeldungen per print, NSLog oder OSLog.
    Zumindest eine dieser Ausgabemöglichkeiten sollte bei TestFlight auch unterstützt werden (bin hier aber
    nicht sicher - evtl. bieten die etwas spezielles Eigenes).

    Ich finde dein Code bietet wenig klare Strukturen. Das wird es schwer machen, Fehler zu erkennen
    oder zu vermeiden. Vielleicht helfen dir die beiden obigen Beispiele als Anregung, etwas klarere Strukturen
    in den Code zu bekommen.
  • Hallo @marcoo und danke für deine Antwort. Ich werde mal mein Glück mit deinen Beispielen versuchen. Wie du allerdings an 5 Zeilen Code sehen kannst, das mein Code keine klaren Strukturen bietet, ist mir ein Rätsel.

    Übrigens: inzwischen kam raus, das der Anwender bei der URL einfach gar nichts eingetragen hatte. Normalerweise hätte das mein Code abfangen müssen. Da muss ich auch noch mal ran.