SwiftUI+Combine - 动态订阅发布者的字典
Posted
技术标签:
【中文标题】SwiftUI+Combine - 动态订阅发布者的字典【英文标题】:SwiftUI+Combine - Dynamicaly subscribing to a dict of publishers 【发布时间】:2021-08-02 14:56:02 【问题描述】:在我的项目中,我拥有大量通过 grpc 流更新的项目。在应用程序内部有几个地方我将这些项目呈现给 UI,我想传播实时更新。
简化代码:
struct Item: Identifiable
var id:String = UUID().uuidString
var name:String
var someKey:String
init(name:String)
self.name=name
class DataRepository
public var serverSymbols: [String: CurrentValueSubject<Item, Never>] = [:]
// method that populates the dict
func getServerSymbols(serverID:Int)
someService.fetchServerSymbols(serverID: serverID) response in
response.data.forEach (name,sym) in
self.serverSymbols[name] = CurrentValueSubject(Item(sym))
// background stream that updates the values
func serverStream(symbols:[String] = [])
someService.initStream() update in
DispatchQueue.main.async
self.serverSymbols[data.id]?.value.someKey = data.someKey
视图模型:
class SampleViewModel: ObservableObject
@Injected var repo:DataRepository // injection via Resolver
// hardcoded value here for simplicity (otherwise dynamically added/removed by user)
@Published private(set) var favorites:[String] = ["item1","item2"]
func getItem(item:String) -> Item
guard let item = repo.serverSymbols[item] else return Item(name:"N/A")
return ItemPublisher(item: item).data
class ItemPublisher: ObservableObject
@Published var data:Item = Item(name:"")
private var cancellables = Set<AnyCancellable>()
init(item:CurrentValueSubject<Item, Never>)
item
.receive(on: DispatchQueue.main)
.assignNoRetain(to: \.data, on: self)
.store(in: &cancellables)
带有子视图的主视图:
struct FavoritesView: View
@ObservedObject var viewModel: QuotesViewModel = Resolver.resolve()
var body: some View
VStack
ForEach(viewModel.favorites, id: \.self) item in
FavoriteCardView(item: viewModel.getItem(item: item))
struct FavoriteCardView: View
var item:Item
var body: some View
VStack
Text(item.name)
Text(item.someKey) // dynamic value that should receive the updates
我肯定错过了一些东西,或者这是一个完全错误的方法,但是我的项目卡没有收到任何更新(我确认后端流处于活动状态并且 serverSymbols 字典正在更新)。任何建议将不胜感激!
【问题讨论】:
可能不是 only 问题,但您应该使用.send()
而不是self.serverSymbols[data.id]?.value
。 Item
是 struct
还是 class
?
项目是一个结构,我已经编辑了问题以添加结构
你能加入minimal reproducible example吗?这里不足以测试您的代码
这是一个相当复杂的设置,通过服务器端流进行后台线程更新,这就是为什么我只发布了简化的相关部分来说明问题。我会试着看看我是否可以把它拆下来足够 MRE 了。
这不起作用我并不感到惊讶。为什么您希望FavoriteCardView
中的item
会自动更新?如果您详细描述您期望发生的事情,将会非常有帮助。
【参考方案1】:
我意识到我犯了一个错误 - 为了接收更新,我需要传递 ItemPublisher 本身。 (我从我的 viewModel 的方法中错误地返回了 ItemPublisher.data)
我已经重构了代码并让 ItemPublisher 使用项目密钥直接从我的存储库中提供数据,所以现在每张卡都使用发布者单独订阅。
现在的最终工作代码:
class SampleViewModel: ObservableObject
// hardcoded value here for simplicity (otherwise dynamically added/removed by user)
@Published private(set) var favorites:[String] = ["item1","item2"]
MainView 和 CardView:
struct FavoritesView: View
@ObservedObject var viewModel: QuotesViewModel = Resolver.resolve()
var body: some View
VStack
ForEach(viewModel.favorites, id: \.self) item in
FavoriteCardView(item)
struct FavoriteCardView: View
var itemName:String
@ObservedObject var item:ItemPublisher
init(_ itemName:String)
self.itemName = itemName
self.item = ItemPublisher(item:item)
var body: some View
let itemData = item.data
VStack
Text(itemData.name)
Text(itemData.someKey)
最后,修改了 ItemPublisher:
class ItemPublisher: ObservableObject
@Injected var repo:DataRepository
@Published var data:Item = Item(name:"")
private var cancellables = Set<AnyCancellable>()
init(item:String)
self.data = Item(name:item)
if let item = repo.serverSymbols[item]
self.data = item.value
item.receive(on: DispatchQueue.main)
.assignNoRetain(to: \.data, on: self)
.store(in: &cancellables)
【讨论】:
以上是关于SwiftUI+Combine - 动态订阅发布者的字典的主要内容,如果未能解决你的问题,请参考以下文章
使用 SwiftUI 和 Combine 根据授权状态有条件地显示视图?
如何使用 Combine 框架和 SwiftUI 发布网络请求的数据