使用 SwiftUI/Combine,如何避免在 ViewModel 中放置可取消项
Posted
技术标签:
【中文标题】使用 SwiftUI/Combine,如何避免在 ViewModel 中放置可取消项【英文标题】:With SwiftUI / Combine, How to avoid to put cancellables in ViewModel 【发布时间】:2020-09-11 15:33:02 【问题描述】:我一直将 DisposeBag 放在带有 RxSwift 的 MVVM 中的 ViewController 中,就像它在本主题中所说的那样:
On ios, for the DisposeBag in MVVM, can it be placed in ViewModel?
但是使用 combine,由于 View 是一个结构并且可取消的不能放置在其中,所以我被困在了解决方案中。
如何在Combine中管理View和VM之间的订阅而不在ViewModel中添加可取消的
或者,在 SwiftUI/Combine 中,没有选择在 VM 中放置可取消项。
在 SiwftUI/Combine 中有一个实现示例:
视图模型
class EquityViewModel: ObservableObject
@Injected private var api: AlphaVantageAPI
private var cancellables = Set<AnyCancellable>()
private let code: String
@Published private var result: Quote?
@Published var price: String = ""
init(code: String)
self.code = code
self.$result
.map
return "\($0?.price ?? 0) €"
.assign(to: &$price)
func addToPortfolio()
func onAppear()
self.api.quote(symbol: self.code).share()
.sink completion in
receiveValue: quote in
self.result = quote.quote
.store(in: &cancellables)
观点
struct EquityView: View
@ObservedObject var viewModel: EquityViewModel
init(viewModel: EquityViewModel)
self.viewModel = viewModel
var body: some View
ZStack
Color("primary").edgesIgnoringSafeArea(.all)
VStack
Text("Stock Price")
.foregroundColor(.white)
.frame(minWidth: 0,
maxWidth: .infinity,
alignment: .topLeading)
.padding()
HStack
Text(self.viewModel.price)
.foregroundColor(.white)
Text("+4.75 %")
.foregroundColor(.white)
.padding(.leading, 20)
.frame(minWidth: 0,
maxWidth: .infinity,
alignment: .topLeading)
.padding()
Button(action: self.viewModel.addToPortfolio, label:
Text("Add to portfolio")
.foregroundColor(.white)
.frame(minWidth: 0,
maxWidth: .infinity,
maxHeight: 30,
alignment: .center)
.background(Color.blue)
.cornerRadius(5)
).padding()
Spacer()
.frame(alignment: .leading)
.onAppear(perform: self.viewModel.onAppear)
【问题讨论】:
您必须将它们存储在某个地方,那么将其保存在创建者附近有什么不好呢?当然,您必须避免参考循环,但您必须始终避免它。顺便说一句,使用sink
而不是assign
,并且不要忘记相应闭包中的[weak self]
。
订阅者应该拥有订阅的所有权。在您的示例中,EquityViewModel
属性会根据发布的值而更改,因此它应该拥有它。或者,您可以自己提供发布者并使用onreceive(_:perform:)
订阅和更改一些状态变量
【参考方案1】:
你搜索的是什么?我无法测试它我不知道您使用的是什么库
class EquityViewMode: ObservableObject
// @Injected private var api: AlphaVantageAPI
var pricePublisher: AnyPublisher<String, Never>
@Published var price: String = ""
init()
// init your publisher like
// pricePublisher = self.api.quote(symbol: self.code)
// .share()
// .map "\($0?.price ?? 0) €"
// .eraseToAnyPublisher()
struct EquityView: View
@ObservedObject var viewModel: EquityViewModel
var handle: AnyCancellable? = nil
init(m:EquityViewMode)
viewModel = m
handle = m.pricePublisher.assign(to: \.price, on: self.viewModel)
var body: some View
Text(viewModel.price)
【讨论】:
以上是关于使用 SwiftUI/Combine,如何避免在 ViewModel 中放置可取消项的主要内容,如果未能解决你的问题,请参考以下文章
使用 SwiftUI、Combine 和 Firebase,我如何在将用户的帐户链接到电子邮件/密码之前验证用户是不是已匿名登录?
忽略 TextField SwiftUI Combine 中输入的左侧空格