了解 SwiftUI 中 StoreKit 2 新功能

news2024/9/22 17:34:26

在这里插入图片描述

在这里插入图片描述

文章目录

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

前言

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 中测试应用内购买。

在这里插入图片描述

构建支付功能

让我们开始构建我们的支付功能,引入 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 构建强大的应用内购买功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1202272.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Clickhouse学习笔记(14)—— Clickhouse监控

ClickHouse 运行时会将一些个自身的运行状态记录到众多系统表中&#xff0c;如下所示&#xff1a; 为了直观方便地监控ck的运行情况&#xff0c;使用Prometheus Grafana 的组合来进行监控 Prometheus 负责收集各类系统的运行指标&#xff1b;Grafana 负责可视化 Prometheus&a…

ASUS华硕ROG枪神2笔记本GL504GS原厂Win10预装OEM系统

链接&#xff1a;https://pan.baidu.com/s/1sqm9NXopSe_mg8v--7fzzA?pwd9dru 提取码&#xff1a;9dru 原厂系统自带显卡网卡声卡等所有驱动、出厂主题壁纸、系统属性华硕专属LOGO标志、Office办公软件、MyASUS华硕电脑管家、控制中心等预装程序 由于时间关系,绝大部分资料…

轻量封装WebGPU渲染系统示例<23>- 可渲染对象添加到多个渲染Pass节点(源码)

渲染和计算混合系统&#xff0c; 可以看做基于算力驱动设计理念的一种实现。 此系统中&#xff0c;可渲染(rendering)/计算(computing)实体可以任意添加到一个渲染器pass节点。若干个这样的节点相关联&#xff0c;就能构成对应的pass node graph&#xff0c;也就实现了整个3D渲…

开播4分钟就被限流的直播回顾!

虽然在双11正式开播之前做过几轮测试&#xff0c;但我们还是踩了坑&#xff01; 而离我们正式开播&#xff0c;才仅仅4分钟而已&#xff01; 双11直播回顾 在双11当天的19:15分&#xff0c;我们开始播放7分半的开播视频&#xff0c;完播两轮之后正好是计划的开播时间。 但…

【KVM-5】KVM架构

前言 大家好&#xff0c;我是秋意零。今天分析的内容是KVM架构。 &#x1f47f; 简介 &#x1f3e0; 个人主页&#xff1a; 秋意零&#x1f525; 账号&#xff1a;全平台同名&#xff0c; 秋意零 账号创作者、 云社区 创建者&#x1f9d1; 个人介绍&#xff1a;在校期间参与…

云课五分钟的一些想法

起源 自中学起&#xff0c;就积极学习和掌握互联网相关知识&#xff0c;到如今已经快30年了。 个人也全程经历了从信息时代的互联网&#xff08;硬&#xff09;到智能时代的大模型&#xff08;软&#xff09;。 整体信息到智能的基础设施&#xff0c;由硬到软&#xff0c;机…

快跑RUSH

欢迎来到程序小院 快跑RUSH 玩法&#xff1a;跑动的小人&#xff0c;点击鼠标左键跳过障碍物&#xff0c;跳过不同的阶梯&#xff0c;经过金币吃掉获取1分&#xff0c;赶紧去快跑PUSH看看你能够获得多少金币哦^^。开始游戏https://www.ormcc.com/play/gameStart/202 html <…

vm ubuntu 新虚拟机的创建

根据自己指定的路径安装好vm后。 创建新的虚拟机。 记录一下&#xff0c;下次用到别再忘记了。 如需转载&#xff0c;注明出处&#xff01; 点赞收藏关注我 以资鼓励 打开vm 软件&#xff0c;点击创建新的虚拟机 选择典型&#xff0c;点击下一步 选择你的ubuntu镜像iso文件 …

Python抓取代码示例

Python 的 requests 和 BeautifulSoup 库&#xff0c;这两个库可以帮助我们发送 请求并解析 HTML 内容。 python pip install requests beautifulsoup4 然后&#xff0c;我们需要导入所需的库。 python import requests from bs4 import BeautifulSoup 接下来&#xff0c…

ceph-deploy bclinux aarch64 ceph 14.2.10【2】vdbench rbd 块设备rbd 测试失败

上篇 ceph-deploy bclinux aarch64 ceph 14.2.10-CSDN博客 安装vdbench 下载vdbench 下载页面 Vdbench Downloads (oracle.com) 包下载 需要账号登录&#xff0c;在弹出层点击同意才能继续下载 用户手册 https://download.oracle.com/otn/utilities_drivers/vdbench/vdb…

在线教育与跨境电商:数字时代的知识传播

随着数字技术的不断发展和全球互联网的普及&#xff0c;在线教育和跨境电商在数字时代崭露头角&#xff0c;共同推动了知识的全球传播。 这两个领域的结合为学生、教育者和知识提供者创造了新的机遇和可能性&#xff0c;同时也带来了一系列有趣的挑战。本文将深入探讨在线教育…

使用项目管理软件优化稿件审批流程

项目管理软件能干什么&#xff1f;可以建立工作流。 如何用项目管理软件建立工作流&#xff1f;今天就以最基础的审批流程为例&#xff0c;看看项目管理软件到底几斤几两&#xff01; 对于内容团队来说&#xff0c;每一篇内容发布前都需要经过多层审核&#xff0c;以免内容不当…

Mac 本地部署thinkphp8【部署环境以及下载thinkphp】

PHP的安装以及环境变量配置 1 PHP安装&#xff1a;在终端输入brew install php 这里是PHP下载的最新的 如果提示‘brew’找不到&#xff0c;自己搜索安装吧&#xff0c; 不是特别难 2 环境变量配置 终端输入vim ~/.bash_profile 输入export PATH"/usr/local/Cellar/php/8.…

统计好书推荐:《统计至简》

电子版网址&#xff1a; Higkoo/Book5_Essentials-of-Probability-and-Statistics - 码云 - 开源中国 (gitee.com) GitHub - Visualize-ML/Book5_Essentials-of-Probability-and-Statistics: Book_5_《统计至简》 | 鸢尾花书&#xff1a;从加减乘除到机器学习&#xff1b;上…

湖南省第六届大学生测绘综合技能大赛 GIS 应用赛项

注意事项&#xff1a; ①确认试题编号正确后再开始作答。 ②所有图件需清晰可辨。 ③新建数值型字段设置数据类型为双精度&#xff0c;数字格式为数值&#xff0c;小数位数默认。 ④答卷中不能出现任何涉密信息&#xff0c;答卷文档转成PDF提交。 1.&#xff08;25 分&#xf…

50个值得关注的生成式AI初创企业【2023】

在线工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 生成式AI初创公司已成为科技界最新、最强大的参与者&#xff0c;它们利用自然语言处理、机器学习和其他形式的人工智能为各种业务用例生成…

VRRP(虚拟路由器冗余协议)标准协议工作机制与优势介绍

VRRP标准协议 文章目录 VRRP标准协议简介VRRP标准协议基本概念VRRP标准协议工作机制主备选举主备倒换非抢占模式抢占模式 VRRP标准协议技术优势VRRP典型组网单备份组主备备份多备份组负载分担 推荐阅读 VRRP标准协议简介 VRRP&#xff08;Virtual Router Redundancy Protocol…

基于单片机智能输液器监控系统的设计

**单片机设计介绍&#xff0c; 基于单片机智能输液器监控系统的设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的智能输液器监控系统可以实现对输液过程的实时监测和控制&#xff0c;以下是一个基本的设计介绍&am…

解锁4D成像雷达「降本」

传感器增强&#xff0c;被视为高阶智驾继续解锁ODD&#xff08;针对自动驾驶及相关功能专门设计的运行条件&#xff09;的关键环节之一。 众所周知&#xff0c;市面上在售新车搭载的智驾系统&#xff0c;由于传感器、算力以及软件算法能力的差异&#xff0c;会导致系统正常运行…

Python常用插件之emoji表情插件的用法

目录 一、概述 二、安装 三、基本用法 四、高级用法 1、自定义emoji表情 2、使用表情符号列表 3、结合使用Emoji和输入文本 4、动态添加emoji表情 5、自定义Emoji的样式 总结 一、概述 在Python中&#xff0c;使用emoji表情已经成为了一种非常流行的趋势。许多开发者…