0

I am getting a Unexpectedly found nil while implicitly unwrapping an Optional value in my Code error when I try to use another view controller to save a new task in a to do list. When I tap a button I open up the entry page which then has a text field where I can enter the text to then create a task item. Here is the code for the main view controller:

  class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    
    @IBOutlet var tableView: UITableView!
    
    private var tasks = [TaskItem]()

    override func viewDidLoad() {
        super.viewDidLoad()
        getAllTasks()
        tableView.delegate = self
        tableView.dataSource = self
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tasks.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = tasks[indexPath.row].title
        return cell
    }
    
    @IBAction func didTapNewTask(){
        let viewContoller = storyboard?.instantiateViewController(identifier: "entry") as! EntryViewController
        viewContoller.title = "New Task"
        viewContoller.update = {
            DispatchQueue.main.async {
                self.getAllTasks()
            }
        }
        navigationController?.pushViewController(viewContoller, animated: true)
    }
    
    //Core Data Functions
    
    //Used to get all our tasks in our Core Data
    func getAllTasks() {
        do {
            tasks = try context.fetch(TaskItem.fetchRequest())
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
        catch {
            print("error getting all tasks \(error)")
        }
    }
    
    //This is used to create a task, setting the properties to those in the parameters and then saving to our Core Data.
    func createTask(title: String, notes: String, difficulty: Int32) {
        let task = TaskItem(context: context)
        task.title = title
        task.notes = notes
        task.difficulty = difficulty
        task.dateCreated = Date()
        
        do {
            try context.save()
            getAllTasks()
        }
        catch {
            
        }
    }

Here is the code for the entry view controller:

class EntryViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var field: UITextField!
    
    var update: (() -> Void)?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        field.delegate = self
        // Do any additional setup after loading the view.
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        saveTask()
        return true
    }
    
    @IBAction func saveTask(){
        let vc = storyboard?.instantiateViewController(identifier: "tasks") as! ViewController
        guard let text = field.text, !text.isEmpty else {
            let alert = UIAlertController(title: "Error", message: "Please input a title" , preferredStyle: UIAlertController.Style.alert)
            alert.addAction(UIAlertAction(title: "Confirm", style: UIAlertAction.Style.default, handler: nil))
            self.present(alert,animated: true,completion: nil)
            return
        }
        vc.createTask(title: text, notes: "Hello", difficulty: 10)
        
        update?()
        
        navigationController?.popViewController(animated: true)
    
    }

The app crashes once I click save the new task but then once I reload the app the task I just created is there.

mag_zbc
  • 6,801
  • 14
  • 40
  • 62
  • Does this answer your question? [What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?](https://stackoverflow.com/questions/32170456/what-does-fatal-error-unexpectedly-found-nil-while-unwrapping-an-optional-valu) – mag_zbc May 12 '21 at 13:32
  • Which line crashes? – Duncan C May 12 '21 at 13:34
  • No it does not, I can't find a time where I use optionals that way. – luke harland May 12 '21 at 13:35
  • self.tableView.reloadData() is the line that crashes. In the main view controller – luke harland May 12 '21 at 13:36
  • Your debug navigator will enlighten you to which row causes the crash. Try replicating it and find the error in Xcode – Harry J May 12 '21 at 23:53

1 Answers1

0
@IBAction func saveTask(){
        let vc = storyboard?.instantiateViewController(identifier: "tasks") as! ViewController
        guard let text = field.text, !text.isEmpty else {
            let alert = UIAlertController(title: "Error", message: "Please input a title" , preferredStyle: UIAlertController.Style.alert)
            alert.addAction(UIAlertAction(title: "Confirm", style: UIAlertAction.Style.default, handler: nil))
            self.present(alert,animated: true,completion: nil)
            return
        }
        vc.createTask(title: text, notes: "Hello", difficulty: 10)
        
        update?()
        
        navigationController?.popViewController(animated: true)
    
    }

The first line of this method is the source of your problem. What you're doing here is making a new instance of the original view controller, not the instance you first came from.

This sort of works for a moment, because you then call createTask on that view controller to make your new task. That's fine, but that method then calls getAllTasks, which then dispatches to the main queue, which then calls reload data on your table.

But your table doesn't exist, because this is a new instance of the view controller which has never had it's view loaded. The table view is an implicitly unwrapped optional, but it's nil when you hit it here.

Your best solution is to pass in a block (like you have with update) to create a new task, and in that block call methods on the original view controller.

jrturton
  • 118,105
  • 32
  • 252
  • 268