2

Im trying to implement a simple to-do feature to my app with Core Data, but I got stuck on loading the added arrays of items. The to-do has 2 views (category view and a corresponding items view). I implemented NSPredicate to filter our the corresponding items based on category, but it only loads the last added item. When I delete the predicate, it shows every single added item obviously.

Also, when I add a new array, I noticed an error for the previous added item in the Debug area:

error save context Error Domain=NSCocoaErrorDomain Code=1570 "parentCategory is a required value." UserInfo={NSValidationErrorObject=<Echieve.Item: 0x6000019fc410> (entity: Item; id: 0xdb0af4c047f4bf03 <x-coredata://F4D9D28E-242A-4BEE-A528-C067AF1F8909/Item/p89>; data: {
done = 0;
parentCategory = nil;
title = 1111;

So Im not sure whether there is a problem in saving or loading of the Items.

var itemArray = [Item]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var selectedCategory: Category? {
    didSet {
        loadItems()
    }
}

override func viewDidLoad() {
    super.viewDidLoad()

    loadItems()

}

// MARK: - Table view data source

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

    return itemArray.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = super.tableView(tableView, cellForRowAt: indexPath)
    let item = itemArray[indexPath.row]
    cell.textLabel?.text = item.title
    cell.accessoryType = item.done ? .checkmark : .none

    return cell
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    itemArray[indexPath.row].done = !itemArray[indexPath.row].done
    saveItems()
    tableView.deselectRow(at: indexPath, animated: true)
}

override func updateModel(at index: IndexPath) {
    context.delete(itemArray[index.row])
    itemArray.remove(at: index.row)
    saveItems()
}

@IBAction func addNewItem(_ sender: Any) {
    var alertTextField = UITextField()
    let alert = UIAlertController(title: "Add new Item", message: "", preferredStyle: .alert)
    alert.addTextField { (textField) in
               textField.placeholder = "enter the name"
            alertTextField = textField
           }
    let action =  UIAlertAction(title: "Add new Item", style: .default) { (action) in

        let newItem = Item(context: self.context)
        newItem.title = alertTextField.text!
        newItem.done = false
        newItem.parentCategory = self.selectedCategory
        self.itemArray.append(newItem)
        self.saveItems()

    }
    let cancel = UIAlertAction(title: "Cancel", style: .default) { (action) -> Void in
        print("Cancel button tapped")
    }
    alert.addAction(action)
    alert.addAction(cancel)

    present(alert,animated: true,completion: nil)
    tableView.reloadData()
}

func saveItems() {

    do {
        try context.save()

    } catch {
        print("error save context \(error) ")
    }
    tableView.reloadData()
}


func loadItems(with request: NSFetchRequest<Item> = Item.fetchRequest() , predicate : NSPredicate? = nil)
{
    let categoryPredicate = NSPredicate(format :"parentCategory.name MATCHES %@",selectedCategory!.name!)
    //print(categoryPredicate)
    if let additionalPredicate = predicate
    {
        request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [categoryPredicate , additionalPredicate])
    }
    else
    {
        request.predicate = categoryPredicate
    }
    print(request.predicate!)
           do{
               itemArray = try context.fetch(request)

           }
           catch
           {
               print("error when fetch result \(error)")
           }
    tableView.reloadData()
}

override func viewWillAppear(_ animated: Bool) {
  super.viewWillAppear(animated)
  loadItems()
}

Thank you very much for anny suggestions.

2
  • Your Item object has one to many relationship with Category object in core data model, right? Commented Jan 23, 2020 at 22:09
  • I really missed that, it was To one..thanks for the help :) Commented Jan 24, 2020 at 11:31

1 Answer 1

1

Make sure your category -> item relationship is 1-to-many. Probably what is happening now is you have 1-to-1 so each time you set parent category on a new item it unsets the previous one.

Also instead of

let categoryPredicate = NSPredicate(format :"parentCategory.name MATCHES %@",selectedCategory!.name!)

just do

let categoryPredicate = NSPredicate(format :"parentCategory = %@", selectedCategory!)
Sign up to request clarification or add additional context in comments.

1 Comment

Thanx a lot, that fixed the problem :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.