非消耗型内购的使用

非消耗型内购的使用

在 iOS 应用中,非消耗型内购通常用于一次性购买永久解锁的功能或内容。以下是一个使用 SwiftUI 实现非消耗型内购的简单示例。

示例场景假设我们有一个应用,用户可以通过内购永久解锁“高级功能”。

实现步骤1. 在 App Store Connect 中配置内购项目

登录 App Store Connect。

为你的应用创建一个非消耗型内购项目(例如,ID 为 com.yourapp.premium)。

2. 在 Xcode 中启用内购功能

打开 Xcode 项目。

在 Signing & Capabilities 中添加 In-App Purchase 能力。

上面的代码使用的是 StoreKit 1,而不是最新的 StoreKit 2。StoreKit 2 是 Apple 在 WWDC 2021 推出的新版本,提供了更简洁、现代化的 API,并且完全基于 Swift 的异步编程模型(async/await)。

如果你希望使用 StoreKit 2 来实现非消耗型内购,以下是更新后的代码示例:

StoreKit 2 实现非消耗型内购1. 配置内购项目

在 App Store Connect 中创建非消耗型内购项目(例如,ID 为 com.yourapp.premium)。

2. 使用 StoreKit 2 实现内购逻辑以下是基于 StoreKit 2 的 SwiftUI 实现:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147import SwiftUIimport StoreKit@MainActorclass StoreManager: ObservableObject { @Published var products: [Product] = [] @Published var isPremiumUnlocked: Bool = false private var updates: Task? = nil init() { updates = observeTransactionUpdates() fetchProducts() checkUnlockedStatus() } deinit { updates?.cancel() } // 获取内购产品 func fetchProducts() { Task { do { let productIDs = ["com.yourapp.premium"] // 替换为你的内购 ID products = try await Product.products(for: productIDs) } catch { print("Failed to fetch products: \(error)") } } } // 购买产品 func purchase(product: Product) { Task { do { let result = try await product.purchase() switch result { case .success(let verification): if case .verified(let transaction) = verification { await transaction.finish() unlockPremium() } case .pending: print("Purchase is pending (e.g., waiting for parental approval)") case .userCancelled: print("User cancelled the purchase") @unknown default: break } } catch { print("Purchase failed: \(error)") } } } // 恢复购买 func restorePurchases() { Task { do { try await AppStore.sync() checkUnlockedStatus() } catch { print("Failed to restore purchases: \(error)") } } } // 检查是否已解锁高级功能 func checkUnlockedStatus() { Task { let productIDs = ["com.yourapp.premium"] // 替换为你的内购 ID for await result in Transaction.currentEntitlements(for: productIDs) { if case .verified(let transaction) = result, transaction.revocationDate == nil { DispatchQueue.main.async { self.isPremiumUnlocked = true UserDefaults.standard.set(true, forKey: "isPremiumUnlocked") } break } } } } // 解锁高级功能 func unlockPremium() { DispatchQueue.main.async { self.isPremiumUnlocked = true UserDefaults.standard.set(true, forKey: "isPremiumUnlocked") } } // 监听交易更新 private func observeTransactionUpdates() -> Task { Task { for await verification in Transaction.updates { if case .verified(let transaction) = verification { await transaction.finish() checkUnlockedStatus() } } } }}struct ContentView: View { @StateObject private var storeManager = StoreManager() var body: some View { VStack { if storeManager.isPremiumUnlocked { Text("高级功能已解锁!") .font(.title) .padding() } else { Text("解锁高级功能") .font(.title) .padding() if let product = storeManager.products.first { Button("购买 \(product.displayName) - \(product.displayPrice)") { storeManager.purchase(product: product) } .padding() } Button("恢复购买") { storeManager.restorePurchases() } .padding() } } .onAppear { // 检查是否已解锁高级功能 storeManager.isPremiumUnlocked = UserDefaults.standard.bool(forKey: "isPremiumUnlocked") } }}@mainstruct InAppPurchaseDemoApp: App { var body: some Scene { WindowGroup { ContentView() } }}

StoreKit 2 的核心改进

基于 async/await 的异步编程:

StoreKit 2 完全支持 Swift 的 async/await,代码更简洁易读。

例如,Product.products(for:) 和 product.purchase() 都是异步方法。

简化的交易管理:

使用 Transaction.currentEntitlements(for:) 检查用户当前的购买状态。

使用 Transaction.updates 监听交易更新。

自动验证交易:

StoreKit 2 会自动验证交易,开发者无需手动处理收据验证。

更安全的 API:

StoreKit 2 提供了更安全的 API,减少开发者的错误使用。

代码说明

StoreManager:

使用 @MainActor 确保 UI 更新在主线程执行。

通过 Product.products(for:) 获取内购产品。

使用 product.purchase() 发起购买。

通过 Transaction.currentEntitlements(for:) 检查用户是否已购买。

ContentView:

显示 UI,包括购买按钮和恢复购买按钮。

根据 isPremiumUnlocked 状态显示不同的内容。

UserDefaults:

用于持久化存储用户是否已解锁高级功能。

运行流程

应用启动时,检查是否已解锁高级功能。

显示购买按钮,用户可以点击购买。

购买成功后,解锁高级功能并更新 UI。

用户可以通过“恢复购买”按钮恢复之前的购买。

注意事项

在真实应用中,建议将购买状态存储在服务器或使用更安全的方式(如 Keychain)。

测试时,使用沙盒账户进行内购测试。

确保在 App Store Connect 中正确配置内购项目。

如果你有其他问题,欢迎随时提问!

当然!以下是对你提供的代码的详细分析。我们将从代码结构、功能实现、潜在问题和改进建议等方面进行探讨。

代码结构分析1. StoreManager 类

职责:管理内购逻辑,包括获取产品、处理购买、恢复购买和检查购买状态。

关键属性:

products: [Product]:存储从 App Store 获取的内购产品。

isPremiumUnlocked: Bool:表示用户是否已解锁高级功能。

关键方法:

fetchProducts():异步获取内购产品。

purchase(product:):发起购买请求。

restorePurchases():恢复购买。

checkUnlockedStatus():检查用户是否已解锁高级功能。

unlockPremium():解锁高级功能并更新状态。

observeTransactionUpdates():监听交易更新。

2. ContentView 视图

职责:显示 UI,包括购买按钮和恢复购买按钮。

关键逻辑:

根据 isPremiumUnlocked 状态显示不同的内容。

调用 StoreManager 的方法处理用户操作。

3. UserDefaults

用途:持久化存储用户是否已解锁高级功能。

功能实现分析1. 获取内购产品

通过 Product.products(for:) 异步获取内购产品。

将获取到的产品存储在 products 数组中。

2. 发起购买

使用 product.purchase() 发起购买请求。

处理购买结果:

如果购买成功,调用 unlockPremium() 解锁高级功能。

如果购买失败或用户取消,打印错误信息。

3. 恢复购买

使用 AppStore.sync() 同步用户的购买记录。

调用 checkUnlockedStatus() 检查用户是否已解锁高级功能。

4. 检查购买状态

使用 Transaction.currentEntitlements(for:) 检查用户当前的购买状态。

如果找到有效的交易记录,解锁高级功能。

5. 监听交易更新

使用 Transaction.updates 监听交易更新。

如果交易状态发生变化(例如退款或撤销),更新购买状态。

潜在问题分析1. UserDefaults 的安全性

问题:UserDefaults 不是存储敏感数据(如购买状态)的安全方式。

建议:使用更安全的方式(如 Keychain)存储购买状态。

2. 未处理网络错误

问题:在 fetchProducts() 和 purchase(product:) 中,网络错误可能导致功能异常。

建议:添加更详细的错误处理逻辑,并向用户显示友好的错误提示。

3. 未处理退款或撤销

问题:如果用户退款或撤销购买,代码中没有显式处理这种情况。

建议:在 observeTransactionUpdates() 中检查 transaction.revocationDate,并根据需要锁定高级功能。

4. 未处理多产品场景

问题:代码假设只有一个内购产品(com.yourapp.premium),无法处理多个产品。

建议:扩展代码以支持多个产品,并根据产品 ID 动态更新 UI。

5. 未处理沙盒测试

问题:代码中没有明确区分沙盒环境和生产环境。

建议:在测试时,确保使用沙盒账户,并添加日志以帮助调试。

改进建议1. 使用 Keychain 存储购买状态

替换 UserDefaults,使用 Keychain 存储购买状态,提高安全性。

2. 增强错误处理

在 fetchProducts() 和 purchase(product:) 中添加详细的错误处理逻辑。

向用户显示友好的错误提示。

3. 处理退款或撤销

在 observeTransactionUpdates() 中检查 transaction.revocationDate,并根据需要锁定高级功能。

4. 支持多产品

扩展代码以支持多个内购产品,并根据产品 ID 动态更新 UI。

5. 添加沙盒测试支持

在测试时,确保使用沙盒账户,并添加日志以帮助调试。

改进后的代码示例以下是改进后的部分代码:

使用 Keychain 存储购买状态123456789101112131415161718192021222324252627import Securityfunc saveToKeychain(key: String, value: Bool) { let data = Data(String(value).utf8) let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: key, kSecValueData as String: data ] SecItemDelete(query as CFDictionary) SecItemAdd(query as CFDictionary, nil)}func loadFromKeychain(key: String) -> Bool { let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: key, kSecReturnData as String: kCFBooleanTrue!, kSecMatchLimit as String: kSecMatchLimitOne ] var dataTypeRef: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &dataTypeRef) if status == errSecSuccess, let data = dataTypeRef as? Data, let value = String(data: data, encoding: .utf8) { return Bool(value) ?? false } return false}

增强错误处理1234567891011121314151617181920212223func purchase(product: Product) { Task { do { let result = try await product.purchase() switch result { case .success(let verification): if case .verified(let transaction) = verification { await transaction.finish() unlockPremium() } case .pending: print("Purchase is pending (e.g., waiting for parental approval)") case .userCancelled: print("User cancelled the purchase") @unknown default: break } } catch { print("Purchase failed: \(error)") // 显示友好的错误提示 } }}

总结你的代码已经实现了基本的内购功能,但可以通过以下方式进一步改进:

提高购买状态存储的安全性。

增强错误处理和用户提示。

支持多产品和处理退款/撤销。

优化沙盒测试支持。

如果你有其他问题或需要进一步的帮助,欢迎随时提问!

相关数据

365bet体育在线滚球 在 TikTok 社群电商,如何让一个爆品诞生
365bet体育在线滚球 征途银子怎么获得和交易(怎么赚取非绑银及任务流程)
bat365app官网入口登录 浵的拼音、浵怎么读?三点水加彤(氵彤)念什么?