了解 SwiftUI 中 StoreKit 2 新功能

在这里插入图片描述

在这里插入图片描述

文章目录

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

前言

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 构建强大的应用内购买功能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

三、机器学习基础知识:Python常用机器学习库(中文文本分析相关库)

文章目录 1、Jieba库1.1 主要函数1.2 词性标注1.3 关键词提取 2、WordCloud库2.1 常见参数2.2 词云绘制 文本分析是指对文本的表示及其特征的提取&#xff0c;它把从文本中提取出来的特征词进行量化来表示文本信息&#xff0c;经常被应用到文本挖掘以及信息检索的过程当中。 1、…

毕业等于失业?那就早点做职业规划

大学生已经不再是稀罕物种了...最佳调侃就是&#xff1a;毕业等于失业....人艰.... 这个只是玩笑&#xff0c;却道破了实质...说到底&#xff0c;找工作依然是竞争关系&#xff0c;跟当年的高考没有什么本质区别....岗位就这么多&#xff0c;毕业生却年年量产.... 哪些找到好…

项目笔记记录

一、node下载版本报错&#xff1a;npm install --legacy-peer-deps 二、Scheduled: 任务自动化调度 Scheduled 标记要调度的方法的注解&#xff0c;必须指定 cron&#xff0c;fixedDelay或fixedRate属性之一 fixedDelay&#xff1a;固定延迟 延迟执行任务&#xff0c;任务在…

Linux ____03、文件类型、属性、修改文件属性(更改文件权限)(命令)

文件类型、属性、修改文件属性 一、文件类型二、文件属性三、修改文件属性1、chgrp&#xff1a;更改文件属组2、chown&#xff1a;更改文件属主&#xff0c;也可以同时更改文件属组3、chmod&#xff1a;更改文件9个属性————————如觉不错&#xff0c;随手点赞&#xff…

想要检测TikTok网络是否安全?这五个网站请收好

Tiktok目前在海外大火&#xff0c;越来越多的人想要进入TikTok的海外市场并捞一桶金。然而&#xff0c;成功并非易事。想要在TikTok中立足&#xff0c;我们必须保证我们的设备、网络环境和网络节点完全符合官方的要求&#xff0c;并且没有任何异常或风险。那么我们该如何设置、…

晶圆代工产能利用率下降,降价大战一触即发 | 百能云芯

晶圆代工行业正面临产能利用率的重大挑战&#xff0c;据悉&#xff0c;联电、世界先进和力积电等主要代工厂纷纷降低明年首季的报价&#xff0c;幅度高达两位数百分比&#xff0c;项目客户降幅更高达15%至20%&#xff0c;各大晶圆代工厂深陷产能利用率六成保卫战。 晶圆代工降价…

【学习笔记】 - GIT的基本操作,IDEA接入GIT以及上传hub

用github蛮多&#xff0c;但git没怎么用&#xff0c;看着视频对着写点笔记以及操作 一、GIT文件的三种状态和模式 已提交(committed) 已提交表示数据已经安全的保存在本地数据库中。 已修改(modified) 已修改表示修改了文件&#xff0c;但还没保存到数据库中。…

AI艺术字比赛;OpenAI GPTs 分享网站;AI项目坟场;农村程序员独立开发者;小红书·大模型与推荐系统 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f251; Alibaba X 堆友「AI造字」主题作品设计大赛 https://d.design/competition/ai-word 堆友 (D.DESIGN) 是一个在线设计平台&#xff0c;…

CLK_CFG_AD9516时钟芯片(配置代码使用说明)

目录 1 概述2 例程功能3 例程端口4 数据时序5 注意事项6 调用例程7附录&#xff08;代码以及寄存器&#xff09; 1 概述 本文用于讲解CLK_CFG_AD9516例程配置代码的使用说明&#xff0c;方便使用者快速上手。 2 例程功能 本例程 是采用verilog hdl编写&#xff0c;实现AD951…

浅谈jvm

前置知识补充 JDK、JRE、JVM是什么&#xff1f;区别与联系&#xff1f; 区别&#xff1a; JDK&#xff08;Java Development Kit&#xff09;&#xff1a;Java开发工具包 主要包括 Java运行环境、Java基础库及 Java工具。 JRE&#xff08;Java Runtime Environment&#xf…

硬盘、U盘的数据恢复利器-供大家学习研究参考

可以快速恢复硬盘、U盘、存储卡等数据 支持快速扫描和底层扫描模式 体验不错 下载地址&#xff1a; https://download.csdn.net/download/weixin_43097956/88530241

在 HarmonyOS 上实现 ArkTS 与 H5 的交互

介绍 本篇 Codelab 主要介绍 H5 如何调用原生侧相关功能&#xff0c;并在回调中获取执行结果。以“获取通讯录”为示例分步讲解 JSBridge 桥接的实现。 相关概念 Web组件&#xff1a;提供具有网页显示能力的 Web 组件。 ohos.web.webview&#xff1a;提供 web 控制能力。 …

thinkphp6 只有默认页能访问 其他404 其他模块404

1.只有默认页能访问 其他页404 同时隐藏index.php 在 public/.htaccess 中添加如下配置&#xff0c;后重启服务 <IfModule mod_rewrite.c>Options FollowSymlinks -MultiviewsRewriteEngine OnRewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-f…

接口测试框架实战(一) | Requests 与接口请求构造

Requests 是一个优雅而简单的 Python HTTP 库&#xff0c;其实 Python 内置了用于访问网络的资源模块&#xff0c;比如urllib&#xff0c;但是它远不如 Requests 简单优雅&#xff0c;而且缺少了许多实用功能。所以&#xff0c;更推荐掌握 Requests 接口测试实战技能&#xff0…

祝贺莱佛士学生获得SDC国际设计大赛新加坡赛区冠军

染色师和调色师协会国际设计大赛&#xff08;SDC International Design Competition&#xff0c;简称SDC国际设计大赛&#xff09;由英国染色家协会&#xff08;Society of Dyers and Colourists&#xff0c;简称SDC&#xff09;举办。 SDC成立于1884年&#xff0c;是国际上最…

IT 基础架构管理需要了解的信息

各行各业的现代组织不断面临创新和扩展的压力。就在十多年前&#xff0c;一个组织可以争取时间&#xff0c;在投资新技术时保持保守&#xff0c;同时仍然保持竞争优势&#xff0c;快进到今天&#xff0c;随着商业实践的变化和新技术的不断涌现&#xff0c;商业和技术领域变得更…

视频号小店怎么做?运营四步骤,快来学习!

大家好&#xff0c;我是电商糖果 2023年因为视频号小店的爆火&#xff0c;想尝试开店的朋友也不少。 但是因为自己是新手小白&#xff0c;对做电商方面了解的也并不多&#xff0c;再加上它是一个才出来一年多的电商平台。对它的很多规则和玩法并不清楚。 所以&#xff0c;这…

Elasticsearch docker-compose 使用 Logstash 从 JSON 文件中预加载数据

在我们创建 Elasticsearch 进行开发时&#xff0c;最简单的办法就是在本地使用 docker-compose 来一键部署一个 Elasticsearch 集群。有时&#xff0c;特别是在准备测试环境时&#xff0c;开发人员希望从一开始就创建包含一些测试数据的数据库容器。我们可以使用 Logstash 来很…

好题分享(2023.11.5——2023.11.11)

目录 前情回顾&#xff1a; 前言&#xff1a; 题目一&#xff1a;补充《移除链表元素》 题目二&#xff1a;《反转链表》 解法一&#xff1a;三指针法 解法二&#xff1a;头插法 题目三&#xff1a; 《相交链表》 题目四&#xff1a;《合并两个有序数列》 题目五&…

【原创】java+swing+mysql办公用品管理系统设计与实现

摘要&#xff1a; 办公用品管理系统是一个设计和实现办公用品库存和使用管理的信息系统。此系统可以提高办公用品的利用率&#xff0c;减少浪费&#xff0c;使办公用品管理更加高效、规范、便捷。本文主要介绍使用javaswingmysql技术去开发实现一个办公用品管理系统。 功能分…