swiftcore-datauikitfatal-error

UIKit / Swift / How to fix fatal error in context.fetch(fetchRequest)?


Got a fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value. I can't find the reason for the error - let results = try context.fetch(fetchRequest).

This is the block where I get the error:

override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        
        let userName = "Max"
        let fetchRequest: NSFetchRequest<User> = User.fetchRequest() //хотим получать данные по юзеру
        fetchRequest.predicate = NSPredicate(format: "name == %@", userName) //пишем предикат для имени
        
        do {
            let results = try context.fetch(fetchRequest)
            if results.isEmpty {
                user = User(context: context) //если нет юзера Макс, то создаем его и помещаем в контекст
                user.name = userName //создали и присвоили имя
                try context.save() //сохраняем
            } else {
                user = results.first //если есть юзер в базе, то сразу присваиваем его в var user: User!
            }
        } catch let error as NSError {
            print(error.localizedDescription)
        }
    }
}

And this is all ViewController code:

import UIKit
import CoreData

class ViewController: UIViewController {
    
    @IBOutlet var tableView: UITableView!
    
    var context: NSManagedObjectContext!
    
    var user: User!
    
    lazy var dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        formatter.timeStyle = .short
        return formatter
    }()
    
    @IBAction func addButtonPressed(_ sender: UIBarButtonItem) {

        let meal = Meal(context: context) //создали объект мил
        meal.date = Date() //присвоили ему текущую дату
        //создаем копию приемов пищи у юзера
        let meals = user.meals?.mutableCopy() as? NSMutableOrderedSet //массив
        meals?.add(meal) //добавляем новый примем пищи meals
        user.meals = meals  //присвоили юзеру новый массив
        
        do {
            try context.save()
            tableView.reloadData()
        } catch let error as NSError {
            print(error.localizedDescription)
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
        
        let userName = "Max"
        let fetchRequest: NSFetchRequest<User> = User.fetchRequest() //хотим получать данные по юзеру
        fetchRequest.predicate = NSPredicate(format: "name == %@", userName) //пишем предикат для имени
        
        do {
            let results = try context.fetch(fetchRequest)
            if results.isEmpty {
                user = User(context: context) //если нет юзера Макс, то создаем его и помещаем в контекст
                user.name = userName //создали и присвоили имя
                try context.save() //сохраняем
            } else {
                user = results.first //если есть юзер в базе, то сразу присваиваем его в var user: User!
            }
        } catch let error as NSError {
            print(error.localizedDescription)
        }
    }
}

// MARK: - UITableViewDataSource

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "My happy meal time"
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return user.meals?.count ?? 0
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as UITableViewCell
        
        //нужно получить конкретный прием пищи и поместить его в конкретную строку
        guard let meal = user.meals?[indexPath.row] as? Meal,
              let mealDate = meal.date
        else { return cell } //если не получилось просто вернули пустую ячейку
        
        cell.textLabel!.text = dateFormatter.string(from: mealDate)
        return cell
    }
}

Solution

  • Ok, the error turned out to be prosaic. Wrong name in:

    let container = NSPersistentContainer(name: "MaelTime") 
    

    instead of MealTime:

    class CoreDataStack {
        
        lazy var persistentContainer: NSPersistentContainer = {
            /*
             The persistent container for the application. This implementation
             creates and returns a container, having loaded the store for the
             application to it. This property is optional since there are legitimate
             error conditions that could cause the creation of the store to fail.
             */
            let container = NSPersistentContainer(name: "MaelTime")
            container.loadPersistentStores(completionHandler: { (storeDescription, error) in
                if let error = error as NSError? {
                    // Replace this implementation with code to handle the error appropriately.
                    // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                    
                    /*
                     Typical reasons for an error here include:
                     * The parent directory does not exist, cannot be created, or disallows writing.
                     * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                     * The device is out of space.
                     * The store could not be migrated to the current model version.
                     Check the error message to determine what the actual problem was.
                     */
                    fatalError("Unresolved error \(error), \(error.userInfo)")
                }
            })
            return container
        }()
        
        func saveContext () {
            let context = persistentContainer.viewContext
            if context.hasChanges {
                do {
                    try context.save()
                } catch {
                    // Replace this implementation with code to handle the error appropriately.
                    // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                    let nserror = error as NSError
                    fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
                }
            }
        }