You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中如何在@Observable类间实现属性绑定与视图同步?

解决多实例InAppPurchaseView同步更新问题

原代码存在的问题

  1. InAppPurchaseViewModel中使用@Binding是错误方向:@Binding需要外部传入绑定源,但你要关联的是单例TransactionObserver的属性,不需要通过Binding传递。
  2. 未正确处理单例属性变化的感知:原设计中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 16:57:22