Search code examples
iosswiftuitableviewrealm

Why displaying Realm data in and embedded tableView does not come easy?


Got stuck with displaying the data from Realm in the table view, embedded in a view controller. Here's some code.

Data model class that handles the scheme of how the data is stored (this is a finance tracking app):

class Entry: Object {
    @objc dynamic var name: String = ""
    @objc dynamic var amount: Int = 0
    @objc dynamic var date: Date?
    var isExpense: Bool = false

    // initialization block dropped for concise post purposes
}

Cell class:

class FinanceOverviewCell: UITableViewCell {

    @IBOutlet weak var entryNameLabel: UILabel!
    @IBOutlet weak var entryAmountLabel: UILabel!

    func updateData(name: String, amount: String) {
        entryNameLabel?.text = name
        entryAmountLabel?.text = amount
    }
}

Finally, view controller:

import UIKit
import RealmSwift

class FinanceOverviewController: UIViewController {

    @IBOutlet weak var financeOverviewTableView: UITableView!
    @IBOutlet weak var currentBalanceLabel: UILabel!

    var realm = try! Realm()
    let tableEntries = try! Realm().objects(Entry.self)
    let entriesManager = EntriesManager()
    var notificationToken: NotificationToken?

    override func viewDidLoad() {
        super.viewDidLoad()
        print(tableEntries)

        self.financeOverviewTableView.reloadData()
        financeOverviewTableView.delegate = self
        financeOverviewTableView.dataSource = self

        // notification token code block dropped for concise post purposes

        // outlets code block removed for concise post purposes

extension FinanceOverviewController: UITableViewDelegate {    
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            realm.beginWrite()
            realm.delete(tableEntries[indexPath.row])
            try! realm.commitWrite()
        }
    }
}

extension FinanceOverviewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tableEntries.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = financeOverviewTableView.dequeueReusableCell(withIdentifier: "FinanceOverviewCell", for: indexPath) as! FinanceOverviewCell
        let tableData = tableEntries[indexPath.row]

        cell.updateData(name: tableData.name, amount: String(tableData.amount))

        return cell
    }
}

The problem is in cellForRowAt func in the view controller. Somehow, though tableData each time holds something proper to add a row, it simply adds nothing. In the app screen I see it as yet another row with placeholder labels comes in.

enter image description here

What could be the issue? I read some reference as well as looked through official example but didn't see any special solution for this case.

Thank you for the support.


Solution

  • Actually working with Realm is not hard, I tried to reproduce your code and everything is work fine. I think it's related to NotificationToken, because every time your collection changed, notification token run block of code and I don't know how you update your tableView. I suggest you to use this extension, I'm using it for months and it's the best way to adopt UITableView with Realm's NotificationToken:

        extension UITableView {
      func applyChanges<T>(changes: RealmCollectionChange<T>) {
        switch changes {
          case .initial: reloadData()
          case .update(let results, let deletions, let insertions, let updates):
            let fromRow = { (row: Int) in return IndexPath(row: row, section: 0) }
            beginUpdates()
            insertRows(at: insertions.map(fromRow), with: .automatic)
            reloadRows(at: updates.map(fromRow), with: .automatic)
            deleteRows(at: deletions.map(fromRow), with: .automatic)
            endUpdates()
          case .error(let error): fatalError("\(error)")
        }
      }
    }
    

    example of usage:

    notificationToken = tableEntries.addNotificationBlock { changes in
      financeOverviewTableView.applyChanges(changes)
    }
    

    source: https://academy.realm.io/posts/meetup-jp-simard-mastering-realm-notifications/