当用户更改全局对象时重新初始化合并发布者
Posted
技术标签:
【中文标题】当用户更改全局对象时重新初始化合并发布者【英文标题】:Reinitialize Combine Publishers When User Changes Global Object 【发布时间】:2021-02-18 04:17:42 【问题描述】:到目前为止,我有一个运行良好的 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($0) ,
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 AppState: ObservableObject
static let shared = AppState()
@Published var workspace: Workspace!
init()
//Setup the workspace for the first time
我可以成功接收来自我的发布者的数据,并且可以成功更改 UI 中的全局 Workspace
。问题是在更改Workspace
后,发布者仍然指向旧的Workspace
,最初在创建获取请求时设置。
当AppState
的workspace
发生变化时,如何提示ProjectModel
重新初始化以更新发布者的状态?
【问题讨论】:
【参考方案1】:实现这一点的典型方法是在您的发布者上使用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
作为整个发布者流的值的来源
【讨论】:
【参考方案2】:在您的ProjectModel
中,我会从AnyCancellable
的Set
切换到特定的,以便您可以取消它:
var cdPublisherCancellable : AnyCancellable?
我会将此发布者的设置移出 init,因为您需要再次调用它:
func setupPublisher()
cdPublisherCancellable?.cancel()
cdPublisherCancellable = CoreDataPublisher(request:)...
然后,由于工作区是共享 AppState 上的已发布属性,我将设置另一个发布者链接来观看它:
var workspaceCancellable : AnyCancellable?
init()
workspaceCancellable = AppState.shared.$workspace.sink workspace in
setupPublisher()
【讨论】:
这真的很有帮助,谢谢。这与戴夫的回答基本相同(据我所知)。 顺便问一下,您确定在覆盖下一行相同的可取消之前必须调用cdPublisherCancellable?.cancel()
吗?这不会有效地取代它吗?
我认为你是对的,出版商应该取消他们自己的 deinit -- 我已经预先准备好对这类事情非常谨慎(尤其是在测试期间)以上是关于当用户更改全局对象时重新初始化合并发布者的主要内容,如果未能解决你的问题,请参考以下文章