📝

[Swiftエラー]オプショナル型の値をアンラップした際にnilが検出されたときに確認するとよい項目

2022/07/07に公開

エラー内容


 上画像のエラーは、私含め、Swift初学者は必ずといってもいいほどぶつかるエラーだと思う。この前、このエラーに悩まされていたがなんとか解決したのでそれについて説明したい。
 このエラーの解消法についてはいろいろあるとは思うが、今回は私が現在開発しているアプリ上で起きたエラーの対処法をメモとして残しておこうと思う。このエラーが検出された際にしたらよい確認事項の1つとして誰かの役に立ったら嬉しく思う。

エラーの原因(今回の場合)

以下が問題のコードである。(必要な部分以外は削除している)

ViewController
import Foundation
import UIKit
import RealmSwift

class EditToDoVC: UIViewController {
   @IBOutlet weak var dateTextField: UITextField!
   
   override func viewDidLoad() {
       super.viewDidLoad()
       configurePicker()
       setToDoData()
       displayData()
   }
   
   var datePicker = UIDatePicker()
   let formatter = DateFormatter()
   
    func configurePicker() {
       let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 50))
       let spaceItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
       let doneItem = UIBarButtonItem(title: "完了", style: .done, target: self, action: #selector(doneDatePicker))
       toolBar.setItems([spaceItem,doneItem], animated: true)
       formatter.dateFormat = "yyyy年 M月d日(EEE)"
       datePicker.date = formatter.date(from: (dateTextField?.text)!)!
       datePicker.datePickerMode = .date
       datePicker.preferredDatePickerStyle = .wheels
       dateTextField?.inputView = datePicker
       dateTextField?.inputAccessoryView = toolBar
   }
   
   @objc func doneDatePicker() {
       formatter.dateFormat = "yyyy年 M月d日(EEE)"
       dateTextField?.text = formatter.string(from: datePicker.date)
       self.view.endEditing(true)
   }
   
   //アプリ内でユーザーが作成したToDoデータをリストの中に配列として格納するメソッド
    func setToDoData() {
       let realm = try! Realm()
       let result = realm.objects(ToDoDataModel.self)
       ToDoDataList = Array(result)
   }
   //上のリストの中からユーザーが表示内容として選んだToDoデータを各部品に入れて表示するメソッド(ToDoDateは、作成したToDoをいつ実行するのかについての日付データ)
   func displayData() {
       formatter.dateFormat = "yyyy年 M月d日(EEE)"
       dateTextField.text = formatter.string(from: todoData.ToDoDate)
   }
}

上記のコードの内容は、元々別でユーザー側が作成したToDoデータ(以下、「データ」とする)のリストから、その時ユーザーが詳細を表示させたいデータを呼び、その情報を各部品に入れ表示させるものである。(今回はDatePickerを表示させて日付を選択し、それをテキストフィールドに表示させる実装のみを説明する)

このコードを実行してみると、下図のようなエラーになる。
結論から言うと、これは各メソッドの実行順序が問題である。
 本来は、「データの取得」→「各部品(ここではdateTextField)へのデータの反映」→「DatePickerの作成(dateTextFieldに表示されている日付をDatePickerの初期値とする)」の順序で実行がされなければならない。しかし、viewDidLoad()内を見てみると、「DatePickerの作成」→「データの取得」→「各部品(ここではdateTextField)へのデータの反映」となってしまっている。
簡潔に言えば、データを取得してそれをtextFieldに表示させ、その表示されている内容からDatePickerを作成しなければいけないところを、データの取得も表示もしないままDatePickerの作成をおこなってしまっているのである。DatePickerはtextFieldに表示されている内容をもとに作成されているので、textFieldに何も表示されていないままDatePickerを作成しようとするとnilが検出されるのは当然である。

解決方法

このエラーの原因は、「メソッドの実行の順序の間違い」であるので、viewDidLoad()内のメソッドの順番を変えればよい。「データの取得」→「各部品へのデータの反映」→「DatePickerの作成」となればよいので、下のようにすればよい。

 override func viewDidLoad() {
        super.viewDidLoad()
	setToDoData()
        displayData()
        configurePicker()
    }

追記

現在通っているスクールのメンターの方に「フォースアンラップ」は使わない方がいいと教えて頂いた。エラーによるアプリのクラッシュを避けるなら確かにそれを使用するのがベターだと思う。ただしその場合、エラーが起きていることに気づかない場合もあるので、開発したアプリはしっかりと動作するか日々確認することが必要なのではないかと思った。

Discussion