Setapp 框架
内容
集成需求
iOS
-
Setapp框架可以集成到为iOS 11.0或更高版本开发的App中。目前,框架还不支持watchOS和tvOS。
-
IOS框架支持的Swift版本为5.2或更高。
-
一个iOS App必须支持自定义URL方案。URL方案必须与App包标识符相同。苹果的相关文档:[定义您App的自定义URL方案](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app)。
macOS
-
应用必须由开发者ID证书签名。
-
必须测试并确认与最新macOS版本的兼容性。
对于支持的macOS版本,我们并没有严格要求。Setapp支持从macOS版本10.15(Catalina)到12.0(Monterey)。但是,我们有针对使用较旧macOS 10.13(High Sierra)- 10.14(Mojave)版本客户的一个冻结版本。因此,如果您的App可以支持一些较旧的macOS版本,您的收入可能会增加。
macOS应用的禁止功能
- 付费功能或应用组件;
- 专有安装程序和更新框架;
- 激活和许可机制;
- 内置商店和内购。
安装和设置框架
安装框架
Swift包管理器
使用Swift包管理器将框架与您的项目链接需要Xcode 12或更高版本。
在您的Package.swift
中添加以下依赖项
dependencies: [
.package(
name: "Setapp",
url: "https://github.com/MacPaw/Setapp-framework.git",
from: "2.0.1")
]
CocoaPods
使用CocoaPods,将以下字符串添加到您的Podfile
pod 'Setapp'
为了支持在搭载Apple Silicon的Macs上使用模拟器,我们已经将二进制格式从通用二进制(fat)框架更改为XCFramework。为了使用最新的框架格式,您需要CocoaPods版本1.9或更高版本以及Xcode版本11.0或更高版本。
手动安装
第一步是获取Setapp Framework。
使用Git子模块添加Framework,请在项目根目录中执行以下Git命令
git submodule add https://github.com/MacPaw/Setapp-framework.git
Setapp Framework文件位于项目目录的Setapp-framework
文件夹中。
第二步是将Framework添加到您的项目中。
现在您有两个选项:作为包安装或作为xcframework安装。区别在于包将正确理解Setapp包类型,而xcframework
选项 1. 将 Setapp 框架作为包添加
作为 git 子模块第一步的替代方案,您也可以通过以下步骤手动下载并添加框架:
- 从我们的最新发行页面 Assets 下载
源代码(zip)
文件。- 将带版本的文件夹
Setapp-framewrok-*
重命名为Setapp-framework
。- 从存档中提取文件,并将解包的
Setapp-framework
复制到您的项目目录。
- 在 XCode 中打开您的项目。
- 将整个
Setapp-framework
文件夹拖放到您的项目中。 - 选择您的应用程序目标。
- 选择“通用”选项卡。
- 在“框架、库和嵌入式内容”部分按
+
键。 - 在“工作区”/“Setapp”组中选择
Setapp
库。
选项 2. 安装为 xcframework
作为 git 子模块第一步的替代方案,您也可以通过以下步骤手动下载并添加框架:
- 在此处下载 Framework: Setapp.xcframework.zip。
- 从存档中提取捆绑包并将解包的
Setapp-framework
复制到您的项目目录。- (仅限 iOS)在此处下载 iOS 资源捆绑包: SetappFramework-Resources-iOS.bundle.zip。
将框架添加到您的项目中。
- 在 XCode 中打开您的项目。
- 选择您的应用程序目标。
- 单击“常规设置”面板。
- 将
Setapp.xcframework
拖放到框架、库和嵌入式内容部分。 - 从“嵌入”栏中的菜单中选择“不嵌入”选项。
- (iOS)从存档中提取 iOS 资源捆绑包并将解包的
SetappFramework-Resources-iOS.bundle
复制到您的项目目录,将其拖到 Xcode 项目中并确保其“目标成员资格”是您的应用程序目标。
有关更多详细信息,请参阅 Xcode 帮助中的“将目标链接到框架和库”。
Carthage
要使用 Carthage,请在您的 Cartfile
文件中指定以下行
github "MacPaw/Setapp-framework"
设置框架
将框架链接到您的应用程序
ℹ️ 如果您正在使用 CocoaPods,则可以跳过此步骤。
将 libSetapp.a
链接到应用程序目标。转到您的项目中的 构建设置
选项卡,并将以下字符串值添加到 其他链接器标志
(OTHER_LDFLAGS
)
如果您正在使用 Swift 包管理器、手动集成框架或使用 Carthage
-force_load "$(BUILT_PRODUCTS_DIR)/libSetapp.a"
⚠️ 您必须严格遵守所提供的说明,以确保您的应用程序在 Setapp 环境中正确运行。
iOS
将公钥添加到您的应用
为了在您的应用和我们的服务之间建立信任,框架需要为每个应用生成一个唯一的公钥。
在Setapp开发者账户中注册iOS应用并生成公钥
-
访问您开发者账户的应用页面,并在“Companion macOS应用”下方点击“添加iOS应用”。
-
输入您的iOS应用在App Store的URL,然后点击生成。
一旦Setapp系统处理了链接,您的应用即可注册成功,并将为您生成Setapp公钥。要下载密钥,请点击URL字段下方显示的链接。
提交应用审查时,您不需要再次指定App Store URL — 这些信息已存储在Setapp系统中。
将公钥作为资源添加到您的项目中
公钥用于操作从Setapp系统接收到的数据。每个Setapp套件中的应用都有一个唯一的公钥,它是框架安全性的一个重要组成部分。
要在Xcode中将公钥添加到您的项目中,请直接将setappPublicKey.pem
密钥文件拖放到导航区域。将新对话框顶部的“需要时复制项”复选框选中。
⚠️ 请注意,iOS与macOS平台的公钥不同。
初始化框架
添加公钥后,您应告知我们的框架其位置。默认情况下,我们假设公钥文件名为 setappPublicKey.pem
并位于应用的主要包中。
开始
SetappManager
类的 start(with:)
方法负责这些初始化操作:
- 为框架提供配置,以便它为您的应用启动;
- 一旦成功激活为 Setapp 用户,就开始报告应用的使用情况。
如果您的应用中有 UIApplicationDelegate
方法,请将以下代码添加到 application(_:, didFinishLaunchingWithOptions:)
函数中
import Setapp
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
)
-> Bool
{
SetappManager.shared.start(with: .default)
return true
}
}
如果您的应用中有 UIWindowSceneDelegate
,请将以下代码添加到 scene(_:, willConnectTo:, options:)
函数中
import Setapp
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
)
{
SetappManager.shared.start(with: .default)
}
}
提供自定义配置
在以下情况下,您需要为 SetappManager
类提供自定义配置:
- 您没有使用主应用包来存储公钥。
- 您已在项目中重命名公钥文件。
提供配置必须在初始化框架时进行。
let configuration = SetappConfiguration(
publicKeyBundle: .main,
publicKeyFilename: "setappPublicKey.pem"
)
SetappManager.shared.start(with: configuration)
为您的应用目标添加自定义URL方案支持
如前所述,在集成要求中,我们使用自定义URL方案为Setapp用户解锁应用的受限功能。要添加URL方案,请按照以下步骤操作:
- 在Xcode中,打开您的项目设置的目标信息标签。
- 展开“URL类型”部分。
- 添加一个新的URL类型,并使用以下参数:
- 标识符:
Setapp
- URL方案:您的束标识符
- 角色:
None
- 标识符:
处理打开URL的请求
一旦完成URL方案设置,您就可以继续添加处理在您的应用中打开URL所需代码了。
如果您在应用中有UIApplicationDelegate
,请将其添加到application(_:open>Description:
import Setapp
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
)
-> Bool
{
if SetappManager.shared.canOpen(url: url) {
return SetappManager.shared.open(url: url, options: options) { result in
switch result {
case let .success(setappSubscription):
print("Successfully unlocked new features!")
print("Setapp subscription:", setappSubscription)
case let .failure(error):
print("Failed to unlock new app features due to the error:", error)
}
}
}
return false
}
}
如果您应用中有UIWindowSceneDelegate
,请将这些代码添加到以下函数中:
scene(_:, willConnectTo:, options:)
scene(_:, openURLContexts:)
import Setapp
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
)
{
SetappManager.shared.start(with: .default)
if SetappManager.shared.canOpen(urlContexts: connectionOptions.urlContexts) {
SetappManager.shared.open(urlContexts: connectionOptions.urlContexts) { result in
switch result {
case let .success(setappSubscription):
print("Successfully unlocked new features!")
print("Setapp subscription:", setappSubscription)
case let .failure(error):
print("Failed to unlock new app features due to the error:", error)
}
}
}
}
func scene(
_ scene: UIScene,
openURLContexts URLContexts: Set<UIOpenURLContext>
)
{
if SetappManager.shared.canOpen(urlContexts: URLContexts) {
SetappManager.shared.open(urlContexts: URLContexts) { result in
switch result {
case let .success(setappSubscription):
print("Successfully unlocked new features!")
print("Setapp subscription:", setappSubscription)
case let .failure(error):
print("Failed to unlock new app features due to the error:", error)
}
}
}
}
}
显示激活结果
框架自动显示激活警报。但是,如果您想自定义此行为,可以将您的展示对象符合至SetappMessagesPresenterProtocol
,并通过SetappManager
类的shared
实例的.setMessagesPresenter(_:)
方法将其提供给框架。
final class CustomMessagesPresenter: SetappMessagesPresenterProtocol {
func present(_ statusMessage: SetappStatusMessage,
options: SetappStatusMessageOptions?) {
switch statusMessage {
case .activationInProgress:
// handle activationInProgress status
case .activationSuccess:
// handle activationSuccess status
case .error(let setappError):
// handle error, you can switch setappError.errorCode
// for more detailed info
}
}
}
监控订阅状态
您可以借助SetappSubscription
对象来监控使用您应用的Setapp成员的订阅状态。您有以下3种监控选项:SetappManager
代理、通知和键值观察(KVO)。您还可以使用Combine进行订阅监控。
委派
只需简单声明一个符合SetappManagerDelegate
协议的类,并为SetappManager
类共享实例的delegate
属性进行配置。
import Setapp
class SetappSubscriptionManagerDelegate: SetappManagerDelegate {
init() {
SetappManager.shared.delegate = self
}
// MARK: SetappManagerDelegate
func setappManager(
_ manager: SetappManager,
didUpdateSubscriptionTo newSetappSubscription: SetappSubscription
)
{
print("Manager:", manager)
print("Setapp subscription:", newSetappSubscription)
}
}
通知
除了委派方法外,您还可以观察SetappManager
对象共享实例的SetappManager.didChangeSubscriptionNotification
通知。从下面的示例中可以看出,管理者是对象,新的Setapp订阅状态位于通知的userInfo
属性的NSKeyValueChangeKey.newKey
键中。
import Setapp
class SetappSubscriptionNotificationObserver {
private var notificationObserver: NSObjectProtocol?
init() {
notificationObserver = NotificationCenter.default
.addObserver(forName: SetappManager.didChangeSubscriptionNotification,
object: SetappManager.shared,
queue: .none) { [weak self] (notification) in
self?.setappSubscriptionDidChange(notification: notification)
}
}
deinit {
notificationObserver.map(NotificationCenter.default.removeObserver(_:))
}
// MARK: Notification
func setappSubscriptionDidChange(notification: Notification) {
guard
let manager = notification.object as? SetappManager,
let newValue = notification.userInfo?[NSKeyValueChangeKey.newKey],
let newSetappSubscription = newValue as? SetappSubscription else {
return
}
print("Manager:", manager)
print("Setapp subscription:", newSetappSubscription)
}
}
键值观察(KVO)
如果您喜欢KVO,可以观察SetappManager
类共享实例的subscription
属性。
import Setapp
class SetappSubscriptionKVOObserver {
private var kvoObserver: NSObjectProtocol?
init() {
kvoObserver = SetappManager.shared
.observe(\.subscription, options: [.new]) { [weak self] (manager, change) in
self?.setappSubscriptionDidChange(manager: manager, change: change)
}
}
// MARK: KVO observation
func setappSubscriptionDidChange(
manager: SetappManager,
change: NSKeyValueObservedChange<SetappSubscription>
)
{
guard let newSetappSubscription = change.newValue else {
return
}
print("Manager:", manager)
print("Setapp subscription:", newSetappSubscription)
}
}
Combine
您还可以使用Combine发布者来监控Setapp订阅。有两种方法可以实现这一点。
直接使用subscription
属性发布者
SetappManager.shared.publisher(for: \.subscription)
使用通知发布者,这样您就可以观察到旧值和新值
NotificationCenter.default.publisher(for: SetappManager.didChangeSubscriptionNotification)
配置后台任务
我们利用后台任务在用户未使用您的应用程序时发送使用报告。这使我们能够确保使用跟踪被送达我们的服务器。注意:当前我们不支持SwiftUI应用程序的后台任务。在我们解决这个问题时,请联系我们商讨实现基于后端的用法报告。
要后台发送带有使用报告的网络请求,您必须在“后台模式”功能组中选中Background fetch
复选框。
- 转到“签名与功能”标签。
- 添加
Background modes
功能。 - 选择
Background fetch
模式。 - 选择
Background processing
模式。
将以下代码添加到您的UIApplicationDelegate
类中
func application(
_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler: @escaping () -> Void
)
{
if SetappManager.isSetappBackgroundSessionIdentifier(identifier) {
SetappManager.shared.backgroundSessionCompletionHandler = completionHandler
}
}
许可的后台任务标识符
对于iOS 13及更高版本,我們也使用后台任务。这意味着,您必须允许Setapp以特定的标识符运行后台任务。为此
- 打开您的
Info.plist
文件。 - 在字典中添加
Permitted background task scheduler identifiers
(BGTaskSchedulerPermittedIdentifiers
)键。 - 将
com.setapp.usageReport
追加到键值数组中。
macOS
设置应用包标识符
对于Setapp,您的应用包标识符必须使用-setapp
后缀,以遵循以下模式
<domain>.<companyName>.<appName>-setapp
例如
com.macpaw.CleanMyMac-setapp
app.macpaw.Gemini-setapp
如果您的应用有额外的可执行文件,它们的包标识符必须遵循以下模式
<domain>.<companyName>.<appName>-setapp.<executableName>
例如
com.macpaw.CleanMyMac-setapp.Menu
要添加包标识符,请按照以下步骤操作
- 访问您开发者账号中的应用页面。您会看到您的应用已经在其中。点击
添加第一个版本。
- 在出现的对话框中输入您的应用包标识符。
在Xcode目标中以及您的开发者账号中,关键地使用相同的包标识符。如果有需要,请更新您的应用程序目标。
⚠️ 一旦设置,您将无法更改包标识符。
📘 包标识符是唯一的、区分大小写的标识符,仅包含字母数字字符(A-Z、a-z、0-9)、点(.)和连字符(-)。请注意,只能使用连字符(U+002D);不要按住Option键。另外,您不得在包标识符中指定应用程序版本。
📘 字符串必须按反向DNS格式编写。例如:您的域名是mycompany.com,您的应用程序名称是MyApp。在这种情况下,您可以将
com.mycompany.myapp-setapp
用作应用程序的包标识符。
添加沙箱临时异常授权
如果您的应用是沙箱化的,您必须添加一个临时异常以启用您的应用中集成的库与Setapp Mach服务的通信。
- 打开您项目中的授权文件。
- 添加
com.apple.security.temporary-exception.mach-lookup.global-name
授权密钥。 - 为
com.apple.security.temporary-exception.mach-lookup.global-name
授权密钥的值数组添加com.setapp.ProvisioningService
字符串(Setapp服务名称)。
向您的应用程序添加公钥
为了在您的应用程序和我们的服务之间建立信任,框架需要为每个应用程序提供唯一的公钥。
在Setapp开发者账户中注册macOS应用程序并生成公钥
- 前往您的开发者账户的应用程序页面,并在您应用程序上点击“
新建版本
”。 - 在
发布信息
的右侧,您可以找到以下提示:对于2.0.0及更高版本的macOS库,在这里找到公钥。
。 - 点击该链接并下载公钥。
将公钥添加到项目中作为资源
公钥用于操作来自Setapp系统的数据。每个Setapp套件中的应用程序都拥有唯一的公钥,它是框架安全的重要组成部分。
要在Xcode中将公钥添加到您的项目中,只需将setappPublicKey.pem
密钥文件拖放到导航器区域。将出现一个新对话框。在对话框顶部选择“如有需要则复制项目
”复选框。
⚠️ 对于macOS应用程序,您只能使用此公钥文件名:setappPublicKey.pem
。
⚠️ 公钥必须位于主应用程序包中。
⚠️ 请注意,iOS与macOS平台的公钥不同。
允许Setapp在macOS 13+上更新您的应用
为了允许Setapp在macOS 13(Ventura)和更高版本上更新您的应用,请在您的应用的Info.plist文件中添加以下内容
<key>NSUpdateSecurityPolicy</key>
<dict>
<key>AllowProcesses</key>
<dict>
<key>MEHY5QF425</key>
<array>
<string>com.setapp.DesktopClient.SetappAgent</string>
</array>
</dict>
</dict>
实现版本说明(最新功能)功能
我们强烈建议将版本说明功能集成到您的应用中以提高用户体验。然而,最终决定权在您手中。
自动显示版本说明
自动显示含有已更新应用版本更改列表的对话框。
在applicationDidFinishLaunching(_:)
方法中调用共享SetappManager
的showReleaseNotesWindowIfNeeded()
函数(或添加到另一个合适的位置,例如在被引导对话框之后)。注意,该函数仅在打开新更新的应用后才会显示对话框。
func applicationDidFinishLaunching(_ aNotification: Notification) {
SetappManager.shared.showReleaseNotesWindowIfNeeded()
}
按需显示版本说明
为了允许用户在需要时查看版本说明,请将相应的选项添加到应用的主菜单中。然后调用共享SetappManager
的showReleaseNotesWindow()
函数。
@IBAction private func showReleaseNotes(_ sender: Any) {
SetappManager.shared.showReleaseNotesWindow()
}
添加电子邮件订阅表单
作为一名开发者,您可能希望在Setapp与您的用户保持联系。我们理解这种需求,并在用户同意的情况下为您提供他们的联系信息。但首先,您需要实现用户权限API来请求用户分享他们的电子邮件地址。这样,您可以创建一个基于权限的活跃用户电子邮件列表。以后,您可以直接从您的开发者账户下载数据。
为了显示对话框,请求用户与当前应用共享电子邮件,调用共享的SetappManager
的askUserToShareEmail()
函数。
@IBAction private func showReleaseNotes(_ sender: Any) {
SetappManager.shared.askUserToShareEmail()
}
当用户做出选择后,您不能再次请他们更改决定。
然而,如果用户在未做出选择的情况下关闭对话框,Setapp将再次显示对话框
- 第二次 - 1天内
- 第三次 - 2天内
- 第四次 - 3天内
- 第5至第7次 - 7天内
- 第8次及以后 - 30天内
ℹ️ 用户权限API需要Setapp应用版本3.2.1或更高版本。如果用户使用的是旧版本的Setapp,则不会显示电子邮件订阅表单。
使用供应商API将应用程序集成到Setapp中
⚠️ 使用供应商API将应用程序集成到Setapp中是我们目前正在积极开发的新功能。大多数功能仍在开发中,因此请勿在生产环境中使用它们。
尽管如此,我们仍想分享主要想法,以便在这个早期阶段获得您的反馈。我们期待在[email protected]或Setapp社区Slack中看到您的评论。
请求授权码以访问Setapp服务器
要开始与Setapp服务器通信,您必须从它请求一个授权(auth)码。授权码的有效期为20分钟,在此期间您必须将授权码传递给您的服务器进行进一步处理。
您的应用程序/服务器通过Vendor API如何与Setapp系统通信
- 您的应用程序从Setapp服务器请求并接收授权码。
- 您的应用程序将其传递给您的服务器。
- 您的服务器将授权码交换为Vendor API的访问令牌和刷新令牌。
- 您的服务器使用获取的令牌通过API进一步与Setapp通信,例如交换订阅信息、使用情况报告等。
您可以使用requestAuthorizationCode
函数获取授权码。该函数需要互联网连接,如果用户的iOS或MacOS设备离线,则会失败并显示相应的错误。
要请求授权码,您必须指定以下参数
clientID
:在您的开发者账户中生成的应用程序客户端ID。如果您在Setapp中拥有多个应用程序,则它们的clientID
必须不同(包括macOS应用程序及其iOS伴侣)。scope
:您希望授权的功能列表。在Swift中,可能的值列在VendorAuthorizationScope
枚举中。在Objective-C中,您必须亲自指定这些值为NSStrings
。其他可能的范围值在Vendor API的GET /authorize方法中提到。
// Make sure an active Setapp subscription is present.
// See subscription monitoring examples on this page for more info.
SetappManager.shared.requestAuthorizationCode(
clientID: "your_vendor_api_authorization_code",
scope: [.applicationAccess]
) { result in
switch result {
case let .success(code):
// Authentication code obtained successfully.
// Use the code to authorize your app or server for Setapp: exchange the auth code for the access token and the refresh token using the Setapp API.
print(code)
case let .failure(error):
// The request has failed.
// See the error message for details.
print(error)
}
}
日志记录
如果您想要扩展或减少日志或将控制台日志路径重写为自定义目标,只需几行代码即可实现。
日志级别
只需通过将 SetappManager
类的 logLevel
属性设置为标准选项之一,您就可以轻松更改日志深度
.verbose
.debug
.info
(默认日志级别).warning
.error
.off
SetappManager.logLevel = .debug
日志覆盖
若要覆盖 Setapp 的日志位置并使用自己的日志记录器,请使用 SetappManager
类的 setLogHandle
方法。此函数接受一个闭包,该闭包接受消息字符串和 SetappLogLevel 参数。
SetappManager.setLogHandle { (message: String, logLevel: SetappLogLevel) in
print("[\(logLevel)]", message)
}
在控制台中查看日志
要在控制台应用中显示框架日志,请按照以下步骤操作
-
打开应用,并在搜索字段中粘贴以下查询
subsystem:com.setapp.fmwk
-
请确保在操作菜单中已选择以下项目
- 包括信息消息
- 包括调试消息
或者,您可以在终端中执行此命令以允许从 Setapp 框架显示调试和信息消息
sudo log config --subsystem com.setapp.fmwk --mode "level:debug"
测试您的应用
有关详细信息,请参阅"测试您的应用"。
Setapp Framework 包装器
Electron
您可以在docs/Electron.md中找到关于将 Setapp 框架集成到您的 Electron 应用中的文档。
集成示例
您可以在示例文件夹中找到集成示例。有
AppKit Sample (Swift|Obj-C, SPM|CocoPods|Manual)
- 这些示例包括 Swift 和 Objective-C 以及所有可用的集成工具(SPM、CocoaPods、手动集成)。详情请参考目标名称。UIKit Sample (Swift|Obj-C, SPM|CocoPods|Manual)
- 这些示例包括 Swift 和 Objective-C 以及所有可用的集成工具(SPM、CocoaPods、手动集成)。详情请参考目标名称。Catalyst Sample
- 手动集成 Setapp 框架。SwiftUI Sample
- 手动集成 Setapp 框架。Electron
应用程序,利用我们的 node.js 包装器将 Setapp 框架集成到 Electron 应用程序中。
更多详细内容,您可以访问 Setapp 开发者文档中的"集成 iOS 框架"。