-7

After more research and reviewing the articles pointed out: I concluded my code was actually correct. I removed the .plist file of my app from the simulator documents folder allowing a new one to be created from my new viewcontrollers.

I am receiving a fatal error. My app does build without error to the simulator; However nothing is displayed and the app crashes. The debugger window states "fata error: unexpectedly found nil while unwrapping an Optional value". I believe the error is because the forKey referring to the lists which references an empty array/

import UIKit

class AllListsViewController: UITableViewController, ListDetailViewControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }



    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return lists.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = makeCell(for: tableView)

        let checklist = lists[indexPath.row]
        cell.textLabel!.text = checklist.name
        cell.accessoryType = .detailDisclosureButton

        return cell
    }

    func makeCell(for tableView: UITableView) -> UITableViewCell {
        let cellIdentifier = "Cell"
        if let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) {
            return cell
        } else {
            return UITableViewCell(style: .default, reuseIdentifier: cellIdentifier)
        }
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let checklist = lists[indexPath.row]
        performSegue(withIdentifier: "ShowChecklist", sender: checklist)

}

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "ShowChecklist" {
            let controller = segue.destination as! ChecklistViewController
            controller.checklist = sender as! Checklist
        } else if segue.identifier == "AddChecklist" {
            let navigationController = segue.destination
                                                as! UINavigationController
            let controller = navigationController.topViewController
                                                as! ListDetailViewController
            controller.delegate = self
            controller.checklistToEdit = nil
        }
    }



    var lists: [Checklist]


    required init?(coder aDecoder: NSCoder) {

        lists = [Checklist]()
        super.init(coder: aDecoder)
        loadChecklists()
        }


    override func tableView(_ tableView: UITableView,
                            commit editingStyle: UITableViewCellEditingStyle,
                            forRowAt indexPath: IndexPath) {
        lists.remove(at: indexPath.row)
        let indexPaths = [indexPath]
        tableView.deleteRows(at: indexPaths, with: .automatic)
    }

    func listDetailViewControllerDidCancel(
        _ controller: ListDetailViewController) {
        dismiss(animated: true, completion: nil)
    }
    func listDetailViewController(_ controller: ListDetailViewController,
                                  didFinishAdding checklist: Checklist) {
        let newRowIndex = lists.count
        lists.append(checklist)
        let indexPath = IndexPath(row: newRowIndex, section: 0)
        let indexPaths = [indexPath]
        tableView.insertRows(at: indexPaths, with: .automatic)
        dismiss(animated: true, completion: nil)
    }
    func listDetailViewController(_ controller: ListDetailViewController,
                                  didFinishEditing checklist: Checklist) {
        if let index = lists.index(of: checklist) {
            let indexPath = IndexPath(row: index, section: 0)
            if let cell = tableView.cellForRow(at: indexPath) {
                cell.textLabel!.text = checklist.name
            }
        }
        dismiss(animated: true, completion: nil)
    }

    override func tableView(_ tableView: UITableView,
                            accessoryButtonTappedForRowWith indexPath: IndexPath) {

        let navigationController = storyboard!.instantiateViewController(
            withIdentifier: "ListDetailNavigationController")
            as! UINavigationController

        let controller = navigationController.topViewController
                as! ListDetailViewController
        controller.delegate = self

        let checklist = lists[indexPath.row]
        controller.checklistToEdit = checklist

        present(navigationController, animated: true, completion: nil)
    }

    func documentsDirectory() -> URL {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        return paths[0]
    }

    func dataFilePath() -> URL {
        return documentsDirectory().appendingPathComponent("Checklists.plist")
    }

    func saveChecklists() {
        let data = NSMutableData()
        let archiver = NSKeyedArchiver(forWritingWith: data)

        archiver.encode(lists, forKey: "Checklists")

        archiver.finishEncoding()
        data.write(to: dataFilePath(), atomically: true)
    }

    func loadChecklists() {
        let path = dataFilePath()
        if let data = try? Data(contentsOf: path) {
            let unarchiver = NSKeyedUnarchiver(forReadingWith: data)

            *lists = unarchiver.decodeObject(forKey: "Checklists") as! [Checklist]* ****The Error occurs on this line****

            unarchiver.finishDecoding()
        }
    }
}
  • 1
    Debug your code and find out which line exactly the program crashes. Check to see if your initializer is really being called. I've never seen the controller initializer being customized like you did. I suspect your `lists` is never initialized. Empty array will not raise an error in your case, empty array is not equivalent to `nil`. – John Doe Nov 07 '16 at 05:43

1 Answers1

0

as! is explicitly unwrapping the optional. as? will prevent an app crash by simply returning nil if downcasting fails.

if let checklist = (NSKeyedUnarchiver(forReadingWith: data)?.decodeObject(forKey: "Checklists")) as? [Checklist] {
    lists = checklist
}
else {
    // set lists as appropriate
}
pscuderi
  • 1,404
  • 12
  • 14
  • 1
    adding to this you probably want to set `lists` to an empty `Checklist` array in the else block – Danoram Nov 07 '16 at 05:43