StoreKit|探索 RevenueCat 解决方案

了解如何使用 RevenueCat 实现 App 内订阅及购买功能。

StoreKit|探索 RevenueCat 解决方案

关于 RevenueCat 的几个常见疑惑

RevenueCat vs Superwall

Reddit 论坛在 2024 年下旬的一个调查中,可以看到 RevenueCat 使用率远高于 Superwall。

https://www.reddit.com/r/iOSProgramming/comments/1ewe01i/what_tools_or_methods_do_you_use_to_manage_your/

RevenueCat 相较于 StoreKit 的优势?

RevenueCat 具有更丰富的用户数据分析功能,除此之外:

  • 远程配置:无需应用更新即可更改付费墙设计和定价
  • A/B 测试:可以测试不同付费墙设计对转化率的影响
  • 触发条件:基于用户行为或事件精准显示付费墙
  • 分析功能:详细的转化率和收入分析

用户最终向谁付款?

使用 RevenueCat 时,用户仍然是向苹果支付,这与直接使用 StoreKit 完全一样。

RevenueCat 只提供付费墙的界面和触发逻辑,但实际交易仍由 StoreKit 处理,符合 App Store 审核规则。

Package vs Offering

在 RevenueCat 中,

  • Offerings 是一组相关产品包的集合。定义了用户在付费墙上看到的内容,包括产品、元数据及可选的 UI 界面。可以用于 A/B 测试不同的价格策略或产品组合。
  • Packages 是产品包,是 Offering 中的一个具体购买选项。包含具体的产品详情(价格、产品ID、周期等)

一个 Offering 就像一个模板或配置,而 Packages 则是该模板内的独立订阅选项或产品类型。

配置 RevenueCat 网页端

配置 App Store Connect API

注册 RevenueCat 之后,按照提示在 App Store Connect 中创建 API 密钥并上传到 RevenueCat 中。

这个密钥是因为让 RevenueCat 有权限访问我们有哪些项目。

配置 In-App Purchase Key

配置该密钥,是为了让 RevenueCat 有权限访问月度经常性收入和销售额等数据。

配置完成后,RevenueCat 已经可以读取到项目中创建的商品信息:

添加 RevenueCat 到 Xcode 项目

安装 RevenueCat 的 Purchases SDK

使用 SPM 安装,仓库地址:

https://github.com/RevenueCat/purchases-ios-spm.git

仅需要勾选RevenueCat 和 RevenueCatUI :

初始化 RevenueCat SDK

只需配置一次 Purchases,通常推荐在 App 文件中完成。配置后,通过访问 SDK 中的.shared 实例即可在整个应用中共享同一实例。

详细教程可参考:https://www.revenuecat.com/docs/getting-started/configuring-sdk

在 App 文件初始化器中配置,继续传递 withAPIKey 参数即可,无需传递appUserID 参数,RevenueCat 会自动生成。

import RevenueCat

@main
struct SampleApp: App {
    init() {
        // 配置 RevenueCat SDK
        Purchases.logLevel = .debug
        Purchases.configure(
            withAPIKey: "appl_xxxx"
        )
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
在 RevenueCat 控制台获取 withAPIKey

启用调试日志

RevenueCat 的 SDK 会记录重要信息和错误,帮助了解后台运行情况。调试日志在 Xcode 或 LogCat 中提供详细的幕后操作日志输出。

在开发阶段,建议启用详细的调试日志:

Purchases.logLevel = .debug

中国大陆用户的代理与配置

Purchases.proxyURL = URL(string: "https://api.rc-backup.com/")!

创建 RevenueCatManager

获取产品套餐列表(Purchases.shared.offerings())

在 RevenueCat 中,使用 Offerings 结构体表示产品套餐。

使用最新的符合 Swift 并发的 Purchases.shared.offerings() 方法来获取产品(Offerings)。避免使用之前的 Purchases.shared.getOfferings() 方法,RevenueCat 官方文档还没有更新。

RevenueCat 官方 SwiftUI 示例项目,也使用 Purchases.shared.offerings() 方法

我在 RevenueCatManager 中,创建一个 loadOfferings() 方法,用于加载 offerings 数据。

然后在 RevenueCatManager 的 initialize() 初始化方法中调用它:

在 App 文件中初始化 RevenueCatManager

最佳实践是在 App 入口文件中,创建 RevenueCatManager 实例,并调用 RevenueCatManager.initialize() 初始化方法。

在 RevenueCat 提供的示例项目中,也是在 App 文件中完成初始化。不过,示例项目使用单例模式,因为它是在 iOS17 之前的项目,还不支持 Observation 框架。

RevenueCat 示例项目

获取当前产品套餐(offerings.current)

一个 Project 中,可能有多个 offering 套餐组合 —— 例如用于 A/B 测试。

默认情况下,一个 Project 只有一个 offering,此时可以通过 offerings.current 来获取默认的 offering 套餐。

if let currentOffering = manager.offerings?.current,
!currentOffering.availablePackages.isEmpty

获取产品列表(offerings.current.availablePackages)

在 RevenueCat 示例项目中,使用 ForEach 遍历 offering.availablePackages 用于展示和购买:

示例项目为了简单,直接在 View 中完成购买。 实际项目,我更推荐创建一个 Manager(或 ViewModel)来处理购买逻辑。

还可以通过.weekly.monthly.annual 等,来获取特定周期的产品:

也可以通过 identifier 指定特定的产品:

let packageById = offerings.offering(identifier:"experiment_group")?.package(identifier: "<package_id>")


发起购买请求(Purchases.shared.purchase())

RevenueCat SDK 提供了一个 Purchases.shared.purchase(package: package) 方法,可以非常方便的发起购买请求。

我在 RevenueCatManager 中创建一个 purchase() 方法,完成商品购买,并格外增加了订阅状态刷新、日志输出等功能:

获取会员过期信息

查询用户订阅状态(Purchases.shared.customerInfo())

RevenueCat 提供了 Purchases.shared.customerInfo() 方法,用于获取用户所有购买和订阅数据。

https://www.revenuecat.com/docs/customers/customer-info#checking-if-a-user-is-subscribed

通过获取 CustomerInfo 中的 entitlements,可以判断用户当前是否拥有某个权益。

如果只有一个权益(大部分情况下适用),可以通过下面这个方式来获取权益:

entitlement_id 可以在 RevenueCat 网页端查询:

如果有多个权益,可以查看更详细的文档:https://www.revenuecat.com/docs/customers/customer-info#checking-if-a-user-is-subscribed

查询用户权益类型(月/季/年/永久)

一个权益(Entitlements)就是一个 App Store Connect 中的订阅群组(Subscription Groups)。

一个订阅群组中,可能有多个订阅产品。同样的,RevenueCat 的一个 权益中,也可以有多个 Product。

每个 Product 有不同的 ID:

可以通过 entitlement.productIdentifier 来判断用户具体的权益产品:

一个权益中,同时只会有一个 Product 中生效。

恢复购买

Restoring Purchases | In-App Subscriptions Made Easy – RevenueCat
Restoring purchases is a mechanism by which your user can restore their in-app purchases, reactivating any content that had previously been purchased from the same store account (Apple, Google, or Amazon).

使用 RevenueCat UI 组件

使用 .presentPaywallIfNeeded 修饰器

首先,导入 SDK:

import RevenueCat
import RevenueCatUI

RevenueCat SDK 提供了一个.presentPaywallIfNeeded()修饰符,用于根据用户的当前权益,自动决定是否显示付费墙界面。

import SwiftUI
import RevenueCat
import RevenueCatUI

struct App: View {
    var body: some View {
        ContentView()
            .presentPaywallIfNeeded(
                requiredEntitlementIdentifier: "pro",
                purchaseCompleted: { customerInfo in
                    print("Purchase completed: \(customerInfo.entitlements)")
                },
                restoreCompleted: { customerInfo in
                    // Paywall will be dismissed automatically if "pro" is now active.
                    print("Purchases restored: \(customerInfo.entitlements)")
                }
            )
    }
}

如果还没有设置自定义的 Paywall 界面,RevenueCat 会自动从 Apple 获取配置的产品和优惠方案,并创建一个默认的付费墙界面:

使用 PaywallView 组件

获取 RevenueCat 销售通知

RevenueCat 并未提供当有新的用户购买时,自动发送邮件通知的功能。

但可以通过 RevenueCat 提供的集成轻松实现,例如 Webhooks、Slack 等。

  • IFTTT:webhook 和邮件通知均需要会员。

Webhooks +