SwiftUI中如何在@Observable类间实现属性绑定与视图同步?
解决多实例InAppPurchaseView同步更新问题
原代码存在的问题
InAppPurchaseViewModel中使用@Binding是错误方向:@Binding需要外部传入绑定源,但你要关联的是单例TransactionObserver的属性,不需要通过Binding传递。- 未正确处理单例属性变化的感知:原设计中ViewModel无法自动感知
TransactionObserver的属性更新,导致视图无法同步刷新。
修正方案
核心思路是利用单例共享数据源+@Observable的自动依赖追踪,让所有视图实例共享同一个状态源,实现同步更新。
1. 修正TransactionObserver(确保主线程更新)
StoreKit的回调可能在后台线程执行,必须在主线程更新UI相关属性,否则SwiftUI无法捕捉到变化:
@Observable class TransactionObserver: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver { static let shared = TransactionObserver() var currentProgressInfo = "" // 封装主线程更新方法 private func updateProgressOnMain(_ info: String) { DispatchQueue.main.async { self.currentProgressInfo = info } } // StoreKit交易回调示例 func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { transactions.forEach { transaction in switch transaction.transactionState { case .purchasing: updateProgressOnMain("正在处理交易...") case .purchased: updateProgressOnMain("交易成功!") SKPaymentQueue.default().finishTransaction(transaction) case .failed: updateProgressOnMain("交易失败:\(transaction.error?.localizedDescription ?? "未知错误")") SKPaymentQueue.default().finishTransaction(transaction) default: break } } } }
2. 重构InAppPurchaseViewModel(转发单例状态)
去掉@Binding,直接引用单例并转发状态,SwiftUI会自动追踪单例属性的变化:
@Observable class InAppPurchaseViewModel { private let transactionObserver = TransactionObserver.shared // 直接转发单例的进度信息,自动同步变化 var currentProgressInfo: String { transactionObserver.currentProgressInfo } // 封装购买逻辑,解耦视图与StoreKit细节 func startPurchase(with productId: String) { guard SKPaymentQueue.canMakePayments() else { transactionObserver.updateProgressOnMain("无法发起支付,请检查设置") return } let request = SKProductsRequest(productIdentifiers: [productId]) request.delegate = transactionObserver request.start() } }
3. 调整InAppPurchaseView(保持原有结构)
视图无需修改核心结构,依然使用独立ViewModel,状态会自动同步:
struct InAppPurchaseView: View { private let viewModel = InAppPurchaseViewModel() var body: some View { VStack(spacing: 16) { if !viewModel.currentProgressInfo.isEmpty { Text(viewModel.currentProgressInfo) .padding() .background(Color.gray.opacity(0.1)) .cornerRadius(8) } Button("发起购买") { viewModel.startPurchase(with: "your.product.id") } .buttonStyle(.borderedProminent) } .padding() } }
同步原理
所有InAppPurchaseViewModel实例都引用同一个TransactionObserver单例,当单例的currentProgressInfo更新时,所有ViewModel的currentProgressInfo计算属性会同步返回最新值,SwiftUI的依赖追踪机制会自动触发所有关联视图的刷新,实现多实例同步更新。
额外注意事项
- 必须在App启动时注册交易观察者:在
App结构体的init或onAppear中调用SKPaymentQueue.default().add(TransactionObserver.shared),否则无法接收StoreKit回调。 - 避免直接在视图中操作单例:通过ViewModel封装业务逻辑,降低耦合度,便于后续测试和维护。
内容的提问来源于stack exchange,提问作者kunass2
相关产品推荐
相关产品推荐

