当用户更改全局对象时重新初始化组合发布者

Reinitialize Combine Publishers When User Changes Global Object

我有一个 Core Data 发布器,目前运行良好。我有一个 Workspace 实体和一个 Project 实体。我使用以下发布者获取给定工作区的所有项目:

class ProjectModel: ObservableObject {
  @Published var projects = [Project]()
  private var cancellableSet: Set<AnyCancellable> = []

  init(){
    CoreDataPublisher(request: Project.getAllProjects(), context: PersistenceController.shared.container.viewContext)
      .sink(
        receiveCompletion: { print([=10=]) },
        receiveValue: { [weak self] items in
          self?.projects = items
        })
      .store(in: &cancellableSet)
  }
}

提取请求 getAllProjects() 位于此处的核心数据实体扩展中,其中 NSPredicate 过滤器基于 UI 中设置的 Workspace 对象。

//Core Data Entity Extension
extension Project{
  @nonobjc public class func getAllProjects() -> NSFetchRequest<Project> {
    let workspace = AppState.shared.workspace as Workspace //<-- The user can change this workspace
  
    let request = NSFetchRequest<Project>(entityName: "\(Self.self)")
    request.sortDescriptors = [NSSortDescriptor(keyPath: \Project.name, ascending: true)]
    request.predicate = NSPredicate(format: "workspace = %@", workspace)
    return request
  }
}

Workspace 对象处于全局状态 class:

class AppState: ObservableObject{
  static let shared = AppState()
  @Published var workspace: Workspace!

  init(){
    //Setup the workspace for the first time
  }
}

我可以成功地从我的发布者那里接收数据,并且我可以在 UI 中成功地更改全局 Workspace。问题是在更改 Workspace 后,发布者仍然指向创建提取请求时最初设置的旧 Workspace

如何在 AppStateworkspace 更改时提示 ProjectModel 重新初始化以更新发布者的状态?

在你的 ProjectModel 中,我将从 AnyCancellableSet 切换到特定的,以便你可以取消它:

var cdPublisherCancellable : AnyCancellable?

我会将此发布者的设置移出 init,因为您需要再次调用它:

func setupPublisher() {
  cdPublisherCancellable?.cancel()
  cdPublisherCancellable = CoreDataPublisher(request:)...
}

然后,由于工作区是在您的共享 AppState 上发布的 属性,我会设置另一个发布者 link 来观看它:

var workspaceCancellable : AnyCancellable?
init() {
  workspaceCancellable = AppState.shared.$workspace.sink { workspace in
    setupPublisher()
  }
}

通常,实现此目的的方法是在发布商上使用 flatMap 运算符。 flatMap 允许您“根据接收到的值创建一个新的发布者,然后使用该发布者的输出作为整个发布者链的输出”。

看起来像这样:

AppState.shared.$workspace.flatMap { workspace in
    let request = NSFetchRequest<Project>(entityName: "\(Project.self)")
    request.sortDescriptors = [NSSortDescriptor(keyPath: \Project.name, ascending: true)]
    request.predicate = NSPredicate(format: "workspace = %@", workspace)
    return CoreDataPublisher(request: request, context: PersistenceController.shared.container.viewContext)
}

这为您提供了一个新发布商:

  • .workspace 属性 在您的应用程序状态上发生变化时,根据新的 Workspace
  • 构建一个新的 CoreDataPublisher
  • 使用 CoreDataPublisher 作为整个发布者流的值来源