fs_tigre
fs_tigre

Reputation: 10738

Deleting items in one-to-many relation in Realm

The following code works fine for adding new ItemLists and adding new Itemss to the ItemLists. What I'm having issues with is deleting all items from a list. In other words I have a button (deleteAllItems) which is supposed to delete all items from the selected list, but the way I have it right now it deletes ALL Items in Realm regardless of what list owns them.

What is the right way to create multiple ItemLists which would contain multiple Items and have the ability to delete all Items from a certain list?

ItemList Object:

class ItemList: Object {
    dynamic var listName = ""
    dynamic var createdAt = NSDate()
    let items = List<Item>()
}

Item Object:

class Item: Object {
    dynamic var productName = ""
    dynamic var createdAt = NSDate()
}

Main Controller:

class ViewController: UIViewController, UITableViewDataSource{

    @IBOutlet weak var tableListOfItems: UITableView!
    @IBOutlet weak var inputProductName: UITextField!
    @IBOutlet weak var inputListName: UITextField!

    var allItems : Results<Item>!

    override func viewDidLoad() {
        super.viewDidLoad()
        updateData()
    }

    @IBAction func addNewList(_ sender: Any) {
        let list = ItemList()
        list.listName = inputListName.text!
        try! realm.write {
            realm.add(list)
        }
    }

    @IBAction func addNewItem(_ sender: Any) {
        let newItem = Item()
        newItem.productName = inputProductName.text!

        let list = realm.objects(ItemList.self).filter("listName = 'List Name Here'").first!

        try! realm.write {
            list.items.append(newItem)
             updateData()
        }
    }

    func updateData(){
        allItems = realm.objects(Item.self)
        tableListOfItems.reloadData()
    }

     /// This deletes every Item in Realm which is not what
     /// I want. I want to delete only items that belong to 
     /// a certain list. I tried...
    /// let list = realm.objects(ItemList.self).filter("listName = 'Default List'").first!
    /// list.items.removeAll()
    @IBAction func deleteAllItems(_ sender: Any) {
       try! realm.write {
           realm.delete(realm.objects(Item.self))
           updateData()
       }
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return allItems.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
        let data = allItems[indexPath.row]
        cell.textLabel?.text = data.productName
        return cell
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == UITableViewCellEditingStyle.delete{
            if let item = allItems?[indexPath.row] {
                try! realm.write {
                    realm.delete(item)
                }
                tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
            }
        }
    }
}

Upvotes: 1

Views: 1289

Answers (1)

David Pasztor
David Pasztor

Reputation: 54716

In your deleteAllItems function you should filter them. Without filtering this is the expected behaviour, since you only specify an object class, so Realm will obviously delete all objects matching that class.

If you want to delete all items from Realm itself that are contained in the List, but keep the empty list, you need to change your deleteAllItems function the following way:

@IBAction func deleteAllItems(_ sender: Any) {
       guard let listToDelete = realm.objects(ItemList.self).filter("listName = %@",listNameToDelete).first else { return }
       try! realm.write {
           for item in listToDelete.items {
               realm.delete(realm.objects(Item.self).filter("productName = %@", item.productName).first)
               updateData()
           }
       }
    }

listNameToDelete should be declared either outside your function in your class or you can declare it inside the function as well depending on what you actually want to achieve with this. Also, this implementation assumes that your listNames and productNames are unique.

Upvotes: 2

Related Questions