了解 SwiftUI 中 StoreKit 2 新功能

了解 SwiftUI 中 StoreKit 2 新功能_第1张图片

在这里插入图片描述

文章目录

    • 前言
    • 配置项目
    • 构建支付功能
    • 总结

前言

StoreKit 为我们提供了通过应用程序获得收入的机会。它允许我们设置应用内购买和订阅的购买流程。StoreKit 2 引入了一种基于现代 Swift 的 API,用于构建类型安全的应用内购买。下面我们将开始关于 StoreKit 2 的系列文章。

配置项目

首先,我们必须在项目的 “Signing & Capabilities” 选项卡中配置应用内购买项目。接下来,应该创建一个 StoreKit 配置文件,以便在没有与 App Store 的网络连接的情况下测试应用内购买功能。前往 “File -> New -> File” 并选择 “StoreKit Configuration File”。

可以创建一个仅本地的配置文件,并将其填充为测试订阅和应用内购买项目。另一种选择是启用 “Sync this file with an app in App Store Connect” 复选框,从 App Store Connect 获取订阅和应用内购买项目列表。

最后一步是使用预定义的 StoreKit 配置文件运行你的应用程序。需要编辑项目的 scheme,并在运行部分的选项标签中选择的 StoreKit 配置文件。现在,已经拥有一个完全配置的项目,允许我们在 Xcode 中测试应用内购买。

了解 SwiftUI 中 StoreKit 2 新功能_第2张图片

构建支付功能

让我们开始构建我们的支付功能,引入 Store 类型来处理与应用内购买相关的所有逻辑。

import StoreKit

@MainActor final class Store: ObservableObject {
    @Published private(set) var products: [Product] = []
    
    init() {}
    
    func fetchProducts() async {
        do {
            products = try await Product.products(
                for: [
                    "123456789", "987654321"
                ]
            )
        } catch {
            products = []
        }
    }
}

正如在上面的示例中所看到的,我们定义了 Store 类型,用于获取和存储将显示在支付屏幕上的产品列表。StoreKit 2 框架提供了 Product 类型,该类型封装了与应用内购买相关的所有逻辑。Product 类型具有一个名为 products 的静态函数,我们可以使用它来通过提供标识符集合来获取产品列表。

struct ContentView: View {
    @StateObject private var store = Store()
    
    var body: some View {
        VStack {
            if store.products.isEmpty {
                ProgressView()
            } else {
                ForEach(store.products, id: \.id) { product in
                    Button {
                        Task {
                            try await store.purchase(product)
                        }
                    } label: {
                        VStack {
                            Text(verbatim: product.displayName)
                                .font(.headline)
                            Text(verbatim: product.displayPrice)
                        }
                    }
                    .buttonStyle(.borderedProminent)
                }
            }
        }
        .task {
            await store.fetchProducts()
        }
    }
}

我们使用 Store 类型来获取和显示可用的应用内购买列表。Product 类型的实例包含了我们需要显示的所有信息,如应用内购买的标题、描述和价格。

Product 类型还具有 purchase 函数,我们可以使用它来启动特定产品的应用内购买流程。它返回一个 PurchaseResult 枚举的实例,定义了三种情况:成功、挂起和用户取消。

@MainActor final class Store: ObservableObject {
    // ...
    
    @Published private(set) var activeTransactions: Set<StoreKit.Transaction> = []
    
    func purchase(_ product: Product) async throws {
        let result = try await product.purchase()
        switch result {
        case .success(let verificationResult):
            if let transaction = try? verificationResult.payloadValue {
                activeTransactions.insert(transaction)
                await transaction.finish()
            }
        case .userCancelled:
            break
        case .pending:
            break
        @unknown default:
            break
        }
    }
}

每当购买结果处于成功状态时,都会提供一个 Transaction 类型的关联值,定义了成功的交易。StoreKit 将交易封装在 VerificationResult 类型中,允许我们验证交易是否正确签名并来自 App Store。

VerificationResult 类型由 StoreKit 2 用于验证数据是否有效且由 App Store 签名。它提供了 payloadValue 计算属性,我们可以使用它来解包已签名数据,或者如果数据未正确签名,则引发错误。

一旦获取了交易,应该解锁用户购买的功能,并在特定交易上调用 finish 函数。请记住,只有在解锁已购买的功能后才应该完成交易。

struct ContentView: View {
    @StateObject private var store = Store()
    
    var body: some View {
        VStack {
            Text("Purchased items: \(store.activeTransactions.count)")
            
            if store.products.isEmpty {
                ProgressView()
            } else {
                if store.activeTransactions.isEmpty {
                    ForEach(store.products, id: \.id) { product in
                        Button {
                            Task {
                                try await store.purchase(product)
                            }
                        } label: {
                            VStack {
                                Text(verbatim: product.displayName)
                                    .font(.headline)
                                Text(verbatim: product.displayPrice)
                            }
                        }
                        .buttonStyle(.borderedProminent)
                    }
                }
            }
        }
        .task {
            await store.fetchProducts()
        }
    }
}

当启用“询问购买”时,购买将变为挂起状态。在这种情况下,交易稍后才会到达,只有在父母批准后才会到达。应该观察 Transaction.updates 流来处理这种类型的交易。我们必须在应用程序启动时开始监视此流,以确保不会错过任何交易。

@MainActor final class Store: ObservableObject {
    @Published private(set) var activeTransactions: Set<StoreKit.Transaction> = []
    private var updates: Task<Void, Never>?
    
    // ...
    
    init() {
        updates = Task {
            for await update in StoreKit.Transaction.updates {
                if let transaction = try? update.payloadValue {
                    activeTransactions.insert(transaction)
                    await transaction.finish()
                }
            }
        }
    }
    
    deinit {
        updates?.cancel()
    }
}

StoreKit 2 提供了一种轻松获取所有活跃订阅和已购买产品的方法。Transaction 类型上的 currentEntitlements 属性列出了所有活跃订阅和未退款的产品。

@MainActor final class Store: ObservableObject {
    @Published private(set) var activeTransactions: Set<StoreKit.Transaction> = []
    // ...
    
    func fetchActiveTransactions() async {
        var activeTransactions: Set<StoreKit.Transaction> = []
        
        for await entitlement in StoreKit.Transaction.currentEntitlements {
            if let transaction = try? entitlement.payloadValue {
                activeTransactions.insert(transaction)
            }
        }
        
        self.activeTransactions = activeTransactions
    }
}

我们可以使用 currentEntitlements 属性来获取每次应用程序启动或更频繁时的所有活跃购买。通过主动监视 currentEntitlements 属性,我们消除了还原购买的需求,因为 currentEntitlements 始终包含最新的活跃订阅和非消耗性购买列表,即使它们是在另一台设备上购买的也是如此。

@main
struct MyApp: App {
    @Environment(\.scenePhase) private var scenePhase
    @StateObject private var store = Store()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(store)
                .task(id: scenePhase) {
                    if scenePhase == .active {
                        await store.fetchActiveTransactions()
                    }
                }
        }
    }
}

总结

这篇文章介绍了如何在 iOS 应用中使用 StoreKit 2 实现应用内购买和订阅功能。主要内容包括项目配置、构建 Paywall 功能、显示产品列表、购买产品、处理交易状态、监控交易更新和获取活跃订阅与购买。通过详细的示例和解释,开发者可以轻松了解如何利用 StoreKit 2 构建强大的应用内购买功能。

你可能感兴趣的:(#,Swift,集,实战,swift,ui,蓝桥杯)