ios - Realm swift : Results getting notifications even though there is no data change (Swift)

标签 ios swift realm

我有一个名为 CardDetails 的模型

public class CardDetails: Object {

  @objc public dynamic var id : Int = 0
  @objc public dynamic var number : String?
  @objc public dynamic var name : String?
  @objc public dynamic var status : String?

    override public static func primaryKey() -> String? {
      return "id"
    }

    override public static func indexedProperties() -> [String] {
      return ["status"]
    }
}

当我们从 API 获取数据时,该对象被写入后台线程

let realm = try Realm()
try realm.write {
  for card in cardsList {
    realm.add(card,update: true)
  }
}

我在 ViewController 中使用了如下的 NotificationToken

var cardResults: Results<CardDetails>!
override func viewDidLoad() {
  super.viewDidLoad()       
  cardResults = realm.objects(CardDetails.self).filter("status = %@", "ACTIVE")
  tokenCards = cardResults.observe { [weak self] (changes: RealmCollectionChange) in
    switch(changes) {
    case .initial, .update:
      self?.setUpCardUI()
    case .error(let error):
            // An error occurred while opening the Realm file on the background worker thread
      fatalError("\(error)")
    }
  }
}

问题是我在每次调用卡片获取端点时都会调用 setUpCardUI,尽管数据没有改变(只有一张事件卡片!)。这会导致意外的 UI 行为。

最佳答案

无论数据是否更改,Realm 都会在更新对象时始终发送修改更改,这很糟糕,但是,这并不意味着我们无法解决问题,甚至无法实现一些不错的改进。

我们不想在观察到更改时重新加载整个 TableView ,而是希望手动插入、删除或重新加载单元格。我写了一个扩展来在 tableview 上执行此操作。

import UIKit

// RealmDiff is just a lightweight struct to make passing the changes around cleaner
// public struct RealmDiff {
//    let deletions: [Int]
//    let insertions: [Int]
//    let modifications: [Int]
//}

extension UITableView {
    func reload(section: Int = 0, with diff: RealmDiff?, update: ((UITableViewCell, IndexPath) -> Void)? = nil) {
        guard let diff = diff else {
            reloadData()
            return
        }

        beginUpdates()
        insertRows(at: diff.insertions.map({ IndexPath(row: $0, section: section) }), with: .automatic)
        deleteRows(at: diff.deletions.map({ IndexPath(row: $0, section: section) }), with: .automatic)

        guard update != nil else {
            reloadRows(at: diff.modifications.map({ IndexPath(row: $0, section: section) }), with: .fade)
            endUpdates()
            return
        }

        endUpdates()

        let indexPaths = diff.modifications
            .map ({ IndexPath(row: $0, section: 0) })
            .filter { indexPathsForVisibleRows?.contains($0) ?? false }

        indexPaths.forEach {
            if let cell = cellForRow(at: $0) {
                update?(cell, $0)
            }
        }
    }
}

默认情况下,这仍会交换所有单元格,这可能仍会导致表格 View 跳转,具体取决于您的布局实现。但是我们也可以传递一个闭包来手动更改单元格上的数据。以下是正在使用的扩展的示例:

tableView.reload(with: diff) { [weak self] cell, indexPath in
    if let cell = cell as? MyCellClass {
        MyCellClass.configure(cell: cell, with: myDataSourceDataArray[indexPath.row], delegate: self)
    }
}

这样您就可以使用同一个单元格,无需重新加载。我发现这种方法非常有效并且可重复使用。

关于ios - Realm swift : Results getting notifications even though there is no data change (Swift),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50673131/

相关文章:

ios - 我什么时候应该关闭 FMDatabaseQueue?

ios - 如何创建 .ipa 文件以便用户可以像 AppStore 一样下载和安装?

ios - 从 XCTestCase 子类访问 App Delegate - 类型错误?

ios - 使用 Swift 5.0 编译的模块无法被 Swift 4.2.1 编译器导入

ios - 如何从 Swift 中访问 CLLocationManager?

swift - 如何确定给定(本地)日期的时区

ios - Realm:如何在 Realm 中添加两个 Result<(Object)>

使用过滤器的 SWIFT RealmGeoQueries

ios - 编译框架在归档时提供位码错误

java - Realm:如果应用程序及其库之一使用 Realm 并设置(两者)defaultConfiguration,那么 defaultInstance 是什么?