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

Swift中多In-App Purchase管理问题求助及示例代码需求

多IAP产品管理完整解决方案(修复恢复失效问题)

我来帮你搞定多IAP的管理问题,先揪出你当前代码里的几个核心坑,再给你完整的优化示例:

问题分析

  1. 恢复逻辑错误:你在paymentQueueRestoreCompletedTransactionsFinished里遍历的是queue.transactions,这是队列里的所有交易,不是苹果返回的恢复交易集合;而且updatedTransactions的.restored分支完全是空的,没处理恢复后的状态更新。
  2. UserDefaults判断冗余:原代码里(UserDefaults.standard.value(forKey: "...") != nil) == true的写法太绕,直接用bool(forKey:)更简洁,因为未设置时默认返回false。
  3. 重复添加观察者:每个购买按钮和恢复按钮里都调用SKPaymentQueue.default().add(self),会导致重复注册观察者,引发多次回调。

修正后的完整代码

import UIKit
import StoreKit

class IAPViewController: UIViewController, SKPaymentTransactionObserver {
    
    // MARK: - UI组件
    @IBOutlet weak var product1BuyButton: UIButton!
    @IBOutlet weak var product1Image: UIImageView!
    @IBOutlet weak var product2BuyButton: UIButton!
    @IBOutlet weak var product2Image: UIImageView!
    
    // MARK: - 产品ID常量(建议统一管理)
    private let productID1 = "example.productID1"
    private let productID2 = "example.productID2"

    override func viewDidLoad() {
        super.viewDidLoad()
        // 只在初始化时添加一次交易观察者
        SKPaymentQueue.default().add(self)
        // 页面加载时就检查已购买状态,更新UI
        updatePurchaseUI()
    }
    
    deinit {
        // 页面销毁时移除观察者,避免内存泄漏
        SKPaymentQueue.default().remove(self)
    }

    // MARK: - 购买按钮点击事件
    @IBAction func didTapBuyPRODUCT1(_ sender: Any) {
        purchaseProduct(with: productID1)
    }

    @IBAction func didTapBuyPRODUCT2(_ sender: Any) {
        purchaseProduct(with: productID2)
    }
    
    // MARK: - 通用购买方法(抽离重复逻辑)
    private func purchaseProduct(with productID: String) {
        guard SKPaymentQueue.canMakePayments() else {
            // 提示用户无法进行购买(比如家长控制开启)
            let alert = UIAlertController(title: "无法购买", message: "请检查设备的购买权限设置", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "确定", style: .default))
            present(alert, animated: true)
            return
        }
        
        let payment = SKMutablePayment(productIdentifier: productID)
        SKPaymentQueue.default().add(payment)
    }

    // MARK: - 恢复按钮点击事件
    @IBAction func restoreButton(_ sender: Any) {
        SKPaymentQueue.default().restoreCompletedTransactions()
    }

    // MARK: - SKPaymentTransactionObserver 核心回调
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch transaction.transactionState {
            case .purchased:
                handlePurchasedTransaction(transaction)
                queue.finishTransaction(transaction)
            case .restored:
                handleRestoredTransaction(transaction)
                queue.finishTransaction(transaction)
            case .failed:
                if let error = transaction.error as? SKError {
                    print("购买失败: \(error.localizedDescription)")
                    // 处理用户取消购买的情况
                    if error.code == .paymentCancelled {
                        print("用户取消了购买")
                    }
                }
                queue.finishTransaction(transaction)
            case .purchasing:
                print("正在处理购买...")
            case .deferred:
                print("购买等待家长批准...")
            @unknown default:
                print("未知交易状态")
                queue.finishTransaction(transaction)
            }
        }
    }
    
    // MARK: - 处理已购买交易
    private func handlePurchasedTransaction(_ transaction: SKPaymentTransaction) {
        let productID = transaction.payment.productIdentifier
        switch productID {
        case productID1:
            UserDefaults.standard.set(true, forKey: "product1purchased")
        case productID2:
            UserDefaults.standard.set(true, forKey: "product2purchased")
        default:
            print("未知产品ID: \(productID)")
        }
        updatePurchaseUI()
        showSuccessAlert()
    }
    
    // MARK: - 处理恢复交易
    private func handleRestoredTransaction(_ transaction: SKPaymentTransaction) {
        // 恢复交易的处理逻辑和购买完全一致
        handlePurchasedTransaction(transaction)
    }
    
    // MARK: - 恢复完成回调(可选:可以在这里统一提示恢复结果)
    func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
        if queue.transactions.isEmpty {
            showAlert(title: "恢复完成", message: "没有已购买的项目需要恢复")
        } else {
            showAlert(title: "恢复完成", message: "已恢复所有已购买项目")
        }
    }
    
    // MARK: - 更新购买状态UI
    private func updatePurchaseUI() {
        // 产品1状态更新
        let isProduct1Purchased = UserDefaults.standard.bool(forKey: "product1purchased")
        product1Image.image = UIImage(named: isProduct1Purchased ? "Product 1 In-App Purchase Logo PURCHASED" : "Product 1 In-App Purchase NOT PURCHASED")
        product1BuyButton.isEnabled = !isProduct1Purchased
        product1BuyButton.setTitle(isProduct1Purchased ? "Purchased" : "Buy", for: .normal)
        
        // 产品2状态更新
        let isProduct2Purchased = UserDefaults.standard.bool(forKey: "product2purchased")
        product2Image.image = UIImage(named: isProduct2Purchased ? "Product 2 In-App Purchase Logo PURCHASED" : "Product 2 In-App Purchase NOT PURCHASED")
        product2BuyButton.isEnabled = !isProduct2Purchased
        product2BuyButton.setTitle(isProduct2Purchased ? "Purchased" : "Buy", for: .normal)
    }
    
    // MARK: - 通用弹窗提示
    private func showSuccessAlert() {
        showAlert(title: "购买成功", message: "已解锁对应功能")
    }
    
    private func showAlert(title: String, message: String) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "确定", style: .default))
        present(alert, animated: true)
    }
}

关键优化点说明

  1. 统一管理产品ID:把产品ID抽成常量,避免硬编码出错,后期修改更方便。
  2. 抽离通用逻辑:把购买、UI更新、弹窗等重复逻辑封装成方法,代码更简洁易维护。
  3. 正确处理恢复交易:在.restored分支调用和购买相同的状态更新逻辑,确保恢复后能正确解锁功能;paymentQueueRestoreCompletedTransactionsFinished只用来提示恢复结果,不用再处理状态。
  4. 优化观察者生命周期:在viewDidLoad添加观察者,deinit移除,避免重复注册和内存泄漏。
  5. 简化UserDefaults判断:直接用bool(forKey:)获取状态,逻辑更清晰。

内容的提问来源于stack exchange,提问作者mt0000

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 07:50:12