Realm 写入事务而不通知 SwiftUI 应用程序中的任何观察者

Realm Write Transaction without Notifying Any Observers in SwiftUI App

我想在我的 Realm 数据库上执行某些写入操作,但不会通知我的 SwiftUI 应用程序中的任何观察者。原因是我正在将我的 Realm 数据库同步到 CloudKit,当 CloudKit 响应记录已成功更新时,我设置了 Realm 对象的 modified 属性以匹配服务器。我不希望从中触发通知,因为这是用户已经在 UI 中进行的更改,并且同步往返不应中断 UI.

由于我的观察者散布在我的应用程序中的各种 ObservableObject 中,因此我将所有令牌合并到我的应用程序首次启动时初始化的单例中。

class RealmSetup{
  static let shared = RealmSetup()
  
  var tokenAlpha: NotificationToken? = nil
  var tokenBravo: NotificationToken? = nil
}

然后他们像这样在其他地方设置:

class AlphaModel: ObservableObject{
  static let shared = AlphModel()

  init(){
    let realm = try! Realm()
    RealmSetup.shared.tokenAlpha = realm.objects(Alpha.self).observe { [weak self] _ in
      self?.objectWillChange.send()
    }
  }
}

...类似地:

class BravoModel: ObservableObject{
  static let shared = BravoModel()

  init(){
    let realm = try! Realm()
    RealmSetup.shared.tokenBravo = realm.objects(Bravo.self).observe { [weak self] _ in
      self?.objectWillChange.send()
    }
  }
}

这是我写的精华:

let realm = try! Realm()
realm.beginWrite()

//Do a bunch of record changes...

//Find the non-nil notification tokens and try not to notify them
var tokensReady = [NotificationToken]()

for token in [RealmSetup.shared.tokenAlpha, RealmSetup.shared.tokenBravo]{
  if let ready = token{
    tokensReady.append(ready)
  }
}

try! realm.commitWrite(withoutNotifying: tokensReady)

我遇到的问题是,当同步完成并尝试提交写入时,我遇到了崩溃:

Incorrect Realm: only notifications for the Realm being modified can be skipped.

很明显,令牌与写入不在同一个领域。据我所知,我的 ObservableObjects 在主线程上,但是如果我将我的编写代码包装在 DispatchQueue.main.async { ... } 中,我 仍然 会崩溃。

我不是线程方面的专家,所以我有点困惑。我怎样才能确保所有这些事情都发生在同一个线程上?还是有更简单的方法来完成我想做的事情?

我会将您的服务器状态存储在一个单独的对象中并创建一个 LinkingObjects 属性:

class Something: Object {
    @objc dynamic var name: String = ""
    @objc dynamic var someValue: String = ""

    let status = LinkingObjects<SomethingServerStatus>(
        fromType: SomethingServerStatus.self,
        property: "object"
    )

    override class func primaryKey() -> String? {
        return "name"
    }

    // ...
}


class SomethingServerStatus: Object {
    @objc dynamic var object: Something? = nil
    @objc dynamic var modified: Bool = false

    // ...
}

确保您 link 对象的服务器状态是这样的:

let objects = [
    Something(name: "zero"),
    Something(name: "one"),
    Something(name: "two"),
]
.map {
    SomethingServerStatus(object: [=11=])
}

try! realm.write {
    realm.add(objects)
}

照常观察:

    let realm = try! Realm()
    self.objects = realm.objects(Something.self)
    notificationToken = self.objects.observe { change in
        switch change {
        case .update(_, _, _, let modifications):
            // do something ...
        default:
            break
        }
    }

当您需要更新服务器状态时,只需获取和修改即可。这不会触发任何侦听器的通知:

    let realm = try! Realm()
    try! realm.write {
        realm.objects(SomethingServerStatus.self)
            .filter("object.name == %@", someName)
            .first?
            .modified
            .toggle()
    }

受@Rob 回答的启发,我想到了使用稍微简单一点的对象关系。使用他的示例,我可以这样做:

//Example of a synced object class
class Something: Object {
  @objc dynamic var name: String = ""
  @objc dynamic var someValue: String = ""
  @objc dynamic var status: Status

  override class func primaryKey() -> String? {
    return "name"
  }
}

//Sync status class
class Status: Object {
  @objc dynamic var modified = false
}

然后当我的同步往返完成时,我可以更新相关对象:

something.status.modified = Date()

...并且 Something 对象的通知不会触发,因为更改发生在相关对象上。谢谢,罗布!