The Setapp iOS Framework can be integrated into apps developed with iOS 10.0 or later. The Framework doesn’t work with the watchOS and the tvOS yet.
The supported Swift version for the iOS Framework is 5.2 or later.
An iOS app must support custom URL schemes. The URL scheme must be the same as the app bundle ID. Related documentation by Apple: Defining a Custom URL Scheme for Your App.
The applications must be signed with a Developer ID certificate.
Compatibility with the latest macOS version must be tested and confirmed.
We don’t have strict requirements for supporting macOS versions. Setapp supports macOS versions from 10.15 (Catalina) to 12.0 (Monterey). However, we have a frozen version for our customers who use older macOS 10.13 (High Sierra) - 10.14 (Mojave) versions. So, if your app can support some of the older macOS versions - your revenue could increase.
Not allowed functionality of macOS apps:
Linking the Framework with your project using the Swift Package Manager requires Xcode 12 or later.
Add the following dependency in your Package.swift
:
dependencies: [
.package(
name: "Setapp",
url: "https://github.com/MacPaw/Setapp-framework.git",
from: "2.0.1")
]
With CocoaPods, add the following string to your Podfile
:
pod 'Setapp'
To support usage of the Simulator on Macs with Apple Silicon, we've changed the source binary format from the universal binary (fat) framework to XCFramework. To work with the latest Framework format, you need CocoaPods version 1.9 or later and Xcode version 11.0 or later.
First step is to get Setapp Framework.
To add the Framework by using Git Submodules, execute the following Git command in your project's root directory:
git submodule add https://github.com/MacPaw/Setapp-framework.git
The Setapp Framework files are located in the Setapp-framework
folder of the project directory.
Second step is to add the Framework to your project.
Now you have 2 options: Install as package or Install as xcframework. The difference is that package will correctly understand Setapp package type, and with xcframework
As alternative to the first step with git submodules, you can also download and add the Framework manually by doing these steps:
- Download the
Source code (zip)
file from Assets on our latest release page.- Rename versioned folder
Setapp-framewrok-*
toSetapp-framework
.- Extract files from the archive and copy the unpacked
Setapp-framework
to your project directory.
Setapp-framework
folder to your project.+
in the Frameworks, Libraries, and Embedded Content section.Setapp
library in Workspace
/Setapp
group.As alternative to the first step with git submodules, you can also download and add the Framework manually by doing these steps:
- Download the Framework here: Setapp.xcframework.zip.
- Extract the bundle from the archive and copy the unpacked
Setapp-framework
to your project directory.- (iOS only) Download the iOS resources bundle here: SetappFramework-Resources-iOS.bundle.zip.
Add the Framework to your project.
Setapp.xcframework
to the Frameworks, Libraries, and Embedded Content section.Do Not Embed
option from the menu in the Embed
column.SetappFramework-Resources-iOS.bundle
to your project directory, drag it to your Xcode project and make sure that it's Target membership
is your application target.For more detailed information, see "Link a target to frameworks and libraries" in the Xcode Help.
To use Carthage, specify the line below in your Cartfile
:
github "MacPaw/Setapp-framework"
ℹ️ If you are using CocoaPods - you can skip this step.
Link libSetapp.a
to the application target. Go to the Build Settings
tab of your project and add the following value string to Other Linker Flags
(OTHER_LDFLAGS
):
If you are using Swift Package Manager, integrating Framework manually, or using Carthage:
-force_load "$(BUILT_PRODUCTS_DIR)/libSetapp.a"
⚠️ You must strictly follow the provided instructions to make your application function correctly within the Setapp environment.
To establish trust between your app and our service, the Framework needs a public key that is unique for every app.
Go to the Apps page of your developer account and click "Add iOS application" below the companion macOS app.
Enter the URL of your iOS app on the App Store, then click Generate.
Once the Setapp system processed the link, your app becomes registered, and the Setapp public key is generated for you. To download the key, click the link that appears below the field with the URL.
You won't need to specify the App Store URL again when submitting the app for review with Setapp — the info is stored in the Setapp system.
A public key is used to operate with the data received from the Setapp system. The public key is unique for every app in the Setapp suite and it is an essential part of the Framework's security.
To add the public key to your project in Xcode, simply drag the setappPublicKey.pem
key file to the navigator area. A new dialog box appears. Select the "Copy items if needed" checkbox on the top of the dialog box.
⚠️ Please note that public keys for iOS & macOS platforms differ.
Once you've added the public key, you should tell our Framework its location. By default, we assume that the public key file is named setappPublicKey.pem
and located in the app's main bundle.
The start(with:)
method of the SetappManager
class is responsible for these initialization operations:
If you have the UIApplicationDelegate
method in your app, add the following code to the application(_:, didFinishLaunchingWithOptions:)
function:
import Setapp
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
)
-> Bool
{
SetappManager.shared.start(with: .default)
return true
}
}
If you have UIWindowSceneDelegate
in your app, add the code below to the scene(_:, willConnectTo:, options:)
function:
import Setapp
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
)
{
SetappManager.shared.start(with: .default)
}
}
You’ll need to provide a custom configuration for the SetappManager
class in the following cases:
Providing configuration must take place while initializing the Framework.
let configuration = SetappConfiguration(
publicKeyBundle: .main,
publicKeyFilename: "setappPublicKey.pem"
)
SetappManager.shared.start(with: configuration)
As already mentioned in Integration requirements, we use custom URL schemes to unlock the restricted functionality of your app for Setapp users. To add a URL scheme, follow these steps:
Setapp
None
Once the URL scheme setup is complete, you can proceed with adding the necessary code to handle the process of opening URLs in your app.
If you have UIApplicationDelegate
in your app, add the following code to the application(_:, open:, options:)
function:
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
}
}
If you have UIWindowSceneDelegate
in your app, add the following code to these functions:
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)
}
}
}
}
}
Framework displays activation alerts automatically. However, if you want to customize this behavior, you can conform SetappMessagesPresenterProtocol
by your presenter object and provide it to the framework using the .setMessagesPresenter(_:)
method of the shared
instance of the SetappManager
class
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
}
}
}
You can monitor the subscription status for the Setapp member who uses your app with the help of the SetappSubscription
object. 3 monitoring options are available for you: SetappManager
delegate, notifications, and the Key-Value Observation (KVO).
Simply declare a class conforming to the SetappManagerDelegate
protocol and set up a delegate
property for the shared
instance of the SetappManager
class.
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)
}
}
In addition to the delegate method, you can observe the SetappManager.didChangeSubscriptionNotification
notification for the shared
instance of the SetappManager
object. As you can see from the example below, the manager is the object, and a new Setapp subscription state is located in the NSKeyValueChangeKey.newKey
key in the userInfo
property of the notification.
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)
}
}
If you prefer KVO, you can observe the subscription
property of the shared
instance of the SetappManager
class.
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)
}
}
We utilize background tasks to send you a usage report when a user doesn't use your application at the moment. This allows us to ensure that usage tracking is delivered to our servers.
To send network requests with usage reports in the background, you must select the Background fetch
checkbox in the Background modes
capability group.
Background modes
capability.Background fetch
mode.Background processing
mode.Add the following code to your UIApplicationDelegate
class:
func application(
_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler: @escaping () -> Void
)
{
if SetappManager.isSetappBackgroundSessionIdentifier(identifier) {
SetappManager.shared.backgroundSessionCompletionHandler = completionHandler
}
}
For iOS 13 and later, we also utilize background tasks. That means that you must allow Setapp to run background tasks with specific identifiers. To do that:
Info.plist
file.Permitted background task scheduler identifiers
(BGTaskSchedulerPermittedIdentifiers
) key to the dictionary.com.setapp.usageReport
to the key values array.The bundle ID of your app for Setapp must use the -setapp
suffix to follow this pattern:
<domain>.<companyName>.<appName>-setapp
For example:
com.macpaw.CleanMyMac-setapp
app.macpaw.Gemini-setapp
If your app has additional executables, their bundle IDs must conform to the following pattern:
<domain>.<companyName>.<appName>-setapp.<executableName>
For example:
com.macpaw.CleanMyMac-setapp.Menu
To add the bundle ID, follow these steps:
Add First Version.
It is critical to use the same bundle ID in the Xcode target of your app and in your developer account. Update your app's target if it needed.
⚠️ You will not be able to change the bundle ID once you set it.
📘 A bundle ID is a unique case-sensitive identifier that contains only alphanumeric characters (A-Z, a-z, 0-9), period (.), and hyphen (-). Please note that only the hyphen-minus sign (U+002D) can be used (don't press the Option key). Also, note that you mustn't specify an app version in the bundle ID.
📘 The string must be written in reverse-DNS format. Example: Your domain is mycompany.com, and your app's name is MyApp. In this case, you can use
com.mycompany.myapp-setapp
as a bundle ID of your app.
If your app is sandboxed, you must add a temporary exception to enable communication between the Library integrated into your app and Setapp Mach services.
com.apple.security.temporary-exception.mach-lookup.global-name
entitlement key.com.setapp.ProvisioningService
string (the Setapp service name) value for the com.apple.security.temporary-exception.mach-lookup.global-name
entitlement key’s value array.As a result, your entitlements file must look similar to the following:
To establish trust between your app and our service, the Frameworks needs a public key that is unique for every app.
New version
on your app.Release info
, you can find a notion For macOS library 2.0.0 and higher, find a public key here.
.The public key is used to operate with the data received from the Setapp system. The public key is unique for every app in the Setapp suite and it is an essential part of the Framework's security.
To add a public key to your project in Xcode, simply drag the setappPublicKey.pem
key file to the navigator area. A new dialog box appears. Select the "Copy items if needed" checkbox on the top of the dialog box.
⚠️ For macOS applications, you can only use this public key filename: (
setappPublicKey.pem
).
⚠️ Public key must be located in the main application bundle.
⚠️ Please note that public keys for iOS & macOS platforms differ.
To allow Setapp to update your app on macOS 13 (Ventura) and higher, please add the following to your apps Info.plist file:
<key>NSUpdateSecurityPolicy</key>
<dict>
<key>AllowProcesses</key>
<dict>
<key>MEHY5QF425</key>
<array>
<string>com.setapp.DesktopClient.SetappAgent</string>
</array>
</dict>
</dict>
We highly recommend implementing the release notes functionality into your app to improve the user experience. However, the final dicision is up to you.
Show a dialog box with a list of changes in the updated app version automatically.
Call the showReleaseNotesWindowIfNeeded()
function of the shared SetappManager
in the applicationDidFinishLaunching(_:)
method (or add it to another appropriate place, for example, after the onboarding dialog of your app). Note that this function reveals a dialog only after opening a newly updated app.
func applicationDidFinishLaunching(_ aNotification: Notification) {
SetappManager.shared.showReleaseNotesWindowIfNeeded()
}
To allow users to view release notes anytime they want, add a corresponding option to the app's main menu. Then call the showReleaseNotesWindow()
function of the shared SetappManager
.
@IBAction private func showReleaseNotes(_ sender: Any) {
SetappManager.shared.showReleaseNotesWindow()
}
As a developer, you might want to stay in touch with your users on Setapp. We understand this intention and we can provide you with a list of their contacts if users will consent. But first, you need to implement a User Permissions API for asking a user to share their email address. Thus, you can create a permission-based email list of your active users. Later, you can download it directly from your Developer Account.
To show a dialog box for asking users to share their email with the current application, call the askUserToShareEmail()
function of the shared SetappManager
.
@IBAction private func showReleaseNotes(_ sender: Any) {
SetappManager.shared.askUserToShareEmail()
}
When a user made a choice, you cannot ask him/her again to change the decision.
However, if the user closes the dialog box without making a choice, Setapp will show the dialog box again:
ℹ️ The User Permissions API requires Setapp application version 3.2.1 or newer. If a user uses an older Setapp version, the email subscription form will not appear.
⚠️ Integrating apps into Setapp using the Vendor API is the next "big thing" we're actively working on. Most features are still under development, so please don't use them in Production yet.
Still, we'd like to share the main ideas in order to get your feedback at this early stage. We're looking forward to your comments at [email protected] or in the Setapp Community Slack.
To start communicating with Setapp's server, you must request an authorization (auth) code from it. The auth code has a 20-minute lifetime, so you must pass the auth code to your server for further processing during this time.
How the communication between your app/server and the Setapp system via the Vendor API works:
You can get the auth code using the requestAuthorizationCode
function. The function requires an internet connection and fails with a corresponding error if a user's iOS or MacOS device was offline.
To request the auth code, you must specify these parameters:
clientID
: the app's client ID generated in your developer account. If you have several apps in Setapp, the clientID
must be different for them (including macOS apps and their iOS companions).scope
: a list of functionalities you wish to authorize for communication with the Setapp system. In Swift, the possible values are listed in the VendorAuthorizationScope
enum. In Objective-C, however, you’ll have to specify the values yourself as NSStrings
.
The other possible functionalities scope values are mentioned in the GET /authorize method of the Vendor API.
// 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)
}
}
If you want to extend or reduce your logs or override the console logs path with your own destination, it's easy to do so with just a few lines of code.
You can easily change your log depth just by setting the logLevel
property of the SetappManager
class to one of the standard options:
.verbose
.debug
.info
(default log level).warning
.error
.off
SetappManager.logLevel = .debug
To override the Setapp’s log destination with your own logger, use the setLogHandle
method of the SetappManager
class. This function takes a closure that accepts the message string and the SetappLogLevel parameter.
SetappManager.setLogHandle { (message: String, logLevel: SetappLogLevel) in
print("[\(logLevel)]", message)
}
To display the Framework logs in the Console app, follow these steps:
Open the app and paste the following query into the Search field:
subsystem:com.setapp.fmwk
Make sure these items have been selected in the Actions menu:
Alternatively, you can allow showing debug & info messages from the Setapp Framework by executing this command in Terminal:
sudo log config --subsystem com.setapp.fmwk --mode "level:debug"
See "Testing your apps" for details.
You can find documentation about integrating Setapp Framework to your Electron app in the docs/Electron.md.
You can find integration samples in the Samples folder. There are:
SetappSample-Catalyst
with manually integrated Setapp Framework.SetappSample-macOS-ObjectiveC
that uses CocoaPods as a dependency manager for Setapp Framework integration.Electron
apps that utilize our node.js wrapper to integrate Setapp Framework into the Electron app.For more details, you can visit "Integrating the iOS Framework" in Setapp Developer Documentation.
link |
Stars: 20 |
Last commit: 1 week ago |
File | SHA-256 checksum |
---|---|
Setapp.xcframework.zip | 20a7daaff28e9402934d84a93ea0b0d64cd530eb26f908caf2a36ef579a71c52 |
Setapp.QRCodeGenerator.zip | eb879fb43253badcf8788be6c619030fb54a24541526d0510be7823d71acf42c |
Setapp-framework-3.1.2-checksums.sha256 | f37712a12a50e6d77a0df1565793cb6848a6288c74db6f3c3dfc3a161c22fd2e |
Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics