Search code examples
iosswiftuitableviewrealm

Make a List with some of the items from Results in Realm Swift


What I want to do is make a List with items from a Results. What I have is a UITableView which was populated from Results. From this table I want to create a List when the user selects rows, but for some reason nothing gets appended to the List when I append items from the Results to the List when the rows are selected.

Here is the code I have:

Realm Object:

import Foundation
import RealmSwift

class Fruit:Object{
    dynamic var fruitName:String = ""
    dynamic var createdAt = NSDate()
}

ViewController:

var fruits: Results<Fruit>!
var fruitSelectionList: List<Fruit>!

override func viewDidLoad() {
    super.viewDidLoad()

    updateFruits()

    tableFruits.setEditing(true, animated: true)
    tableFruits.allowsMultipleSelectionDuringEditing = true
}    

func updateFruits(){
    fruits = realm.objects(Fruit.self).sorted(byKeyPath: "fruitName")
}

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

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    if let index = fruitSelectionList?.index(of: fruits[indexPath.row]) {
        try! realm.write {
            fruitSelectionList?.remove(at: index)
        }
    } else {
        try! realm.write {
            // nothing gets appended here, why?
            fruitSelectionList?.append(fruits[indexPath.row])
        }
    }
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
    if let index = fruitSelectionList?.index(of: fruits[indexPath.row]) {
        fruitSelectionList?.remove(at: index)
    }
}

The funny thing is that similar code without using Realm works just fine...

Plain Swift code that works when not using Realm:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate  {

    var fruits = ["Apples", "Oranges", "Grapes", "Watermelon", "Peaches"]

    var newFruitList:[String] = []

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.allowsSelection = false
        tableView.setEditing(true, animated: true)
        tableView.allowsMultipleSelectionDuringEditing = true
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

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

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {  
        let cell = UITableViewCell()
        cell.textLabel?.text = fruits[indexPath.row]
        return cell
    }
    @IBAction func makeSelection(_ sender: Any) {
        tableView.allowsMultipleSelectionDuringEditing = true
        tableView.setEditing(true, animated: true)
    }
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        let item = fruits[indexPath.row]
        if let index = newFruitList.index(of: item) {
            newFruitList.remove(at: index)
        } else {
            newFruitList.append(fruits[indexPath.row])
        }
    }
    func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
        if let index = newFruitList.index(of: fruits[indexPath.row]) {
            newFruitList.remove(at: index)
        }
    }
}

How can I make a List with some items from a Results?

FYI - The only reason I need to create the List is to be able to add and remove items on a table row selection otherwise I would stick with just Results.


Solution

  • Your code is not the same at all when not using Realm. The issue has nothing to do with Realm, it is about wrongly declaring the Lists.

    You declare them as implicitly unwrapped optionals (which you shouldn't do in the first place), then try to append a nil list. You cannot append nil, you need an empty List in the first place. If you didn't use safe unwrapping of the optionals, you would get a runtime exception instead of a nil result.

    Declare fruitSelectionList as an empty List and you won't have this issue.

    var fruits: Results<Fruit>?
    var fruitSelectionList = List<Fruit>()
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if let index = fruitSelectionList?.index(of: fruits[indexPath.row]) {
            fruitSelectionList.remove(at: index)
        } else {
            fruitSelectionList.append(fruits[indexPath.row])
        }
    }
    

    Moreover, fruitSelectionList is not managed by Realm, since only Results are automatically updating, so you don't need to put your actions on fruitSelectionList inside write transactions. You also don't actually need fruitSelectionList to be of type List, it could be an Array as well.

    Just a general piece of advice: if you need your selections to be persisted, I would advise you to update your Fruit model class to have a selected property of type Bool and update that property when a row of the tableView is selected.