AppflowSDK
平台:iOS 版本:v1.0.9
1. SDK集成
AppflowSDK为iOS开发人员提供了一个集成方法进行选择
- 通过CocoaPods集成
通过CocoaPods集成
target 'MyApp' do
use_frameworks!
pod 'AppflowSDK', '~> 1.0.9'
end
保存并执行pod install,然后使用以.xcworkspace为后缀的文件打开项目。
pod install
注意:在命令行中执行 pod search AppflowSDK。如果显示的 AppflowSDK 版本不是最新版本,执行 pod repo update 操作以更新本地仓库的内容。有关CocoaPods的更多信息,请参阅 CocoaPods。
2. 初始化SDK
添加配置文件
下载 appflow-app-token.json 文件,将其添加到项目 targets 中。如需则选择复制项。创建组。添加到 targets。
导入头文件
在项目的 AppDelegate 文件中导入头文件。
import AppflowSDK
设置
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//AppflowSDK initialization
Appflow.shared.configure()
return true
}
启用 SDK 日志(可选)
您可以在初始化 SDK 之前启用 SDK 日志输出
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
//AppflowSDK initialization
Appflow.shared.configure()
//Whether to enable in-app purchase related logs, the default is not
Appflow.shared.setPurchasesLogs(enabled: true)
//Whether to enable the log related to the buried point, the default is not
Appflow.shared.setAnalyticsLogs(enabled: true)
return true
}
3. 购买
准备好工作 a. 在苹果商店中配置产品信息
b. 在 appflow 平台上添加产品信息
获取内购产品 ID
Appflow.shared.getPurchaseProductIds { productIdArray, error in
}
API 参考
getPurchaseProductIds | |
---|---|
error = nil | 成功回调 |
error != nil | 失败回调 |
error: Error | |
---|---|
code | 错误代码 |
message | 错误信息 |
返回参数:数据类型 | 说明 |
---|---|
productIdArray: [string] | 返回 Appflow 平台配置的产品 ID |
获取内购的 SKU 信息
展示产品 若要获取产品SKU信息,您需要调用
根据 getSkuDetails
接口,您需要传入内购产品ID的Set <product_Ids>,返回值是一个字典,其中 product_id
是键,值为 SKProduct
对象
Appflow.shared.getSkuDetails(productIds: Set(productIDs)) { skuDetailInfo, error in
};
API 参考
getSkuDetails | |
---|---|
error = nil | 成功回调 |
error != nil | 失败回调 |
error: Error | |
---|---|
code | 错误代码 |
message | 错误信息 |
请求参数:数据类型 | 说明 |
---|---|
productIds: Set | 该参数是苹果后台配置的产品ID |
返回参数:数据类型 | 说明 |
---|---|
skuDetailInfo: [String:SKProduct] | 键值对形式,返回SKProduct |
获取到 SKProduct
后,可以获取与内购相关的信息,开发者可以在自己的项目中保存这些信息。后续的支付操作需要使用。
SKProduct
请参考苹果官方文档::
检查支付是否可用
let canMakePurchases = Appflow.shared.canMakePayment()
此接口返回类型为 'bool' 的数据。True:表示可以拉起支付。False:表示不能拉起支付
进行购买
要开始购买过程,调用 purchaseProduct 函数,它将 SKProduct
对象作为参数
注意:内购完成后,必须上传userInfo进行报告,这主要用于用户数据归因。
- 如果开发者集成了AppflowSDK(Appflow.shared.purchaseSKProduct)的购买方法,则无需在订阅后处理和报告用户信息,此步骤已自动完成(V1.0.7版后的版本将自动报告,V1.0.7版前的版本需要开发者手动报告)
- 如果开发者没有集成AppflowSDK的商品购买方法,开发者需要手动处理。用户完成购买后,使用此方法 uploadUserInfo 报告用户信息,其中UserId不是必填项
// After V1.0.7, it will be automatically reported after subscription
Appflow.shared.purchaseSKProduct(skProduct) { transaction, subscriber, error, canceled in
if canceled || error != nil {
print("Canceled")
} else {
print("Completed")
}
}
// Before V1.0.7, the subscription must be reported manually, otherwise it will lead to data loss
Appflow.shared.purchaseSKProduct(skProduct) { transaction, subscriber, error, canceled in
if canceled || error != nil {
print("Canceled")
} else {
// Manually report user information after subscription is complete
Appflow.shared.uploadUserInfo()
}
}
闭包将返回
SKPaymentTransaction
、权限字典、错误(如果发生)以及表示是否用户取消购买过程的布尔值
Users can use **IMSubscriber.isActive** to determine the subscription status. true: subscribed\purchased; false: unsubscribed\unpurchased
IMSubscriber.expireAt, Return the expiration time of the current subscription. You can compare `expireAt` with the current time and process the subscription status
API 参考
purchaseSKProduct | |
---|---|
error = nil | 成功回调 |
error != nil | 失败回调,注意:请勿通过ERROR确定用户的订阅状态。请使用:isActive 或 expireAt |
error: Error | |
---|---|
code | 错误代码 |
message | 错误信息 |
请求参数:数据类型 | 说明 |
---|---|
product: SKProduct | 产品信息 |
返回参数:数据类型 | 说明 |
---|---|
transaction: SKPaymentTransaction | 支付队列中的对象。 [SKPaymentTransaction |
isCanceled: Bool | 取消支付 |
entitlement: IMSubscriber | 说明 |
---|---|
productId: String | product id |
expireAt: Int64 | 订阅过期时间(毫秒) |
isActive: Bool | 产品订阅/购买状态,true:已订阅/已购买;false:未订阅/未购买 |
imEntitlement: [IMEntitlement] | 检查当前状态的权限 |
imSubscription: [IMSubscription] | 按产品ID查询订阅详情 |
IMEntitlement | 说明 |
---|---|
id: 字符串 | 产品分组ID |
expireAt: Int64 | 订阅过期时间(毫秒) |
isActive: Bool | 产品订阅/购买状态,true:已订阅/已购买;false:未订阅/未购买 |
productId: String | product id |
IMSubscription | 产品ID |
---|---|
状态: IMSubscriberStatus | SubscriptionState_State |
expireAt: Int64 | 订阅过期时间(毫秒) |
cancelAt: Int64 | 取消订阅时间(毫秒) |
willRenewTo: 字符串 | 下一个切换的订阅ID:产品ID |
originalTransactions: [IMSubscriptionOriginalTransaction] | 原始交易 |
IMSubscription | 产品ID |
---|---|
originalTxid: 字符串 | 原始订单备注 |
expireAt: Int64 | 订阅过期时间(毫秒) |
startAt: Int64 | 开始订阅时间(毫秒) |
检查用户状态
要检查用户是否有任何活动订阅,请调用函数 hasActiveSubscription
Appflow.shared.hasActiveSubscription({ [weak self] subscriber, error in
guard let `self` = self else { return }
var status = "pro not active"
if error == nil {
if subscriber.isActive {
status = "pro active"
}
self.statusLabel.text = "Subcription status: \(status)"
}
})
Users can use **IMSubscriber.isActive** to determine the subscription status. true: subscribed\purchased; false: unsubscribed\unpurchased
IMSubscriber.expireAt, Return the expiration time of the current subscription. You can compare `expireAt` with the current time and process the subscription status
恢复购买
要恢复用户购买,请调用 restorePurchases 函数。
Appflow.shared.restorePurchases { (subscriber, error) in
if error == nil {
if subscriber.isActive {
// Purchase restored and have active entitlment
}else {
// Purchase restore finished but user don't have active entitlment
}
}
}
Users can use **IMSubscriber.isActive** to determine the subscription status. true: subscribed\purchased; false: unsubscribed\unpurchased
IMSubscriber.expireAt, Return the expiration time of the current subscription. You can compare `expireAt` with the current time and process the subscription status
UploadUserInfo
简介:如果您需要将应用程序中的 userId 与 Appflow 平台关联起来,那么开发者需要选择一个合适的时间向 Appflow 平台报告 userId,例如,当您的应用程序登录时。其中 useId 是在 App 中定义的唯一标识符。
Appflow.shared.uploadUserInfo(userId: "xxx") { _, _ in }
展开
的数据。它支持固定字段:username(用户名)、email(电子邮件)、phone(电话)、gender(性别)、age(年龄)。
在UploadUserInfo API中添加json字符串字段以支持开发者上传类型为map
- 字段
- 所有键值的长度限制为128,按toString字符计算
- 固定字段包括用户名、电子邮件、电话、性别、年龄,请参阅下表,性别是数字,具体意义见表格
- 对于用户添加的额外字段类型没有限制,可以使用数字和字符串
- 如果您需要报告与用户相关的数据,请通过以下方式报告:
字段 | 类型 |
---|---|
username | 字符串 |
字符串 | |
phone | 字符串 |
gender | 数字(女性 = 1,男性 = 2,其他 = 3) |
age | 数字 |
//uploadUserInfo to extraAttribute
Appflow.shared.uploadUserInfo(userId: "app_user_idxxxxx", extraAttribute: extraAttribute) {[weak self] _, _ in
}
4. 事件追踪
您可以通过以下方式将统计事件发送到Appflow后端
//Send an event with params, If you have no arguments you can just pass 'nil'
uploadBigDataWithType(eventName: String, params: [AnyHashable : Any]?)
示例
//Send an event with no parameters
Appflow.shared.uploadBigDataWithType(eventName: "test_event", params: nil)
//Send an event with parameters
var params:[AnyHashable : Any] = [AnyHashable : Any]()
params["name"] = "abc"
arams["age"] = 18
Appflow.shared.uploadBigDataWithType(eventName: "test_event", params: params)
如果您想检查事件是否成功报告,可以按以下代码打开AnalyticsLogs
Appflow.shared.setAnalyticsLogs(enabled: true)
5. 推送通知
a. 功能
请启用应用程序目标中的功能→推送通知选项
b. 在AppDelegate中请求推送通知权限
private func requestPushNotificationsPermissions() {
let userNotificationCenter = UNUserNotificationCenter.current()
userNotificationCenter.requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, error in
print("Permission granted: \(granted)")
if granted {
self?.getNotificationSettings()
}
}
}
c. 权限请求完成,获取通知设置
private func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
print("Notification settings: \(settings)")
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
d. 成功注册APNs并报告DeviceToken
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceToken = deviceToken.reduce("", { $0 + String(format: "%02X", $1) })
Appflow.shared.purchases.uploadDeviceInfo(deviceToken: deviceToken) { _, _ in
}
}
6. 支付墙
SDK 提供了显示付费产品的快捷方式。在Appflow平台设置样式后,您可以调用方法来显示它
Appflow.shared.loadPaywallToPurachase {[weak self] result, msg in
guard let `self` = self else { return }
if result == false {
//if false , Developers can jump to the native payment page
}
}
7. 归属
Appflow支持Appsflyer、Adjust、Branch、Apple搜索广告、Facebook Ads和自定义归属数据上传,您需要调用方法
Appsflyer
要上传Appsflyer归属数据,开发者需要访问appsflyer SDK,并在AppsFlyerLibDelegate方法中报告数据,请参考以下示例,其中**networkUserId**不能为空
extension AppDelegate: AppsFlyerLibDelegate {
func onConversionDataFail(_ error: Error) {
//appsflyer is fail
}
func onConversionDataSuccess(_ installData: [AnyHashable : Any]) {
// It's important to include the network user ID
Appflow.shared.updateAttribution(installData, source: .appsflyer, networkUserId: AppsFlyerLib.shared().getAppsFlyerUID())
}
}
Adjust
要上传Adjust归属数据,开发者需要访问Adjust SDK,并在AdjustDelegate方法中报告数据,请参考以下示例
extension AppDelegate: AdjustDelegate {
//Callback for the first app installation
func adjustAttributionChanged(_ attribution: ADJAttribution?) {
// Just pass Adjust attribution to Adapty SDK
if let attribution = attribution?.dictionary() {
//networkUserId:Adjust.adid()
Appflow.shared.updateAttribution(attribution, source: .adjust, networkUserId: Adjust.adid())
}
}
}
Branch
要上传Adjust归属数据,开发者需要访问Adjust SDK,并在RegisterDeepLinkHandler块中报告数据,请参考以下示例
// MARK: branch init
func branchInit(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) {
Branch.getInstance().initSession(launchOptions: launchOptions) { (data, error) in
if let data = data {
Appflow.shared.updateAttribution(data, source: .branch,networkUserId: Appflow.shared.getAppUserId())
//When Branch reports data, setIdentity must be set for data association.
Branch.getInstance().setIdentity(Appflow.shared.getAppUserId())
}
}
}
Facebook Ads
上传 Facebook Ads 归因数据时,请参考以下示例,其中 **networkUserId** 不可为空
Appflow.shared.updateAttribution([:], source: .facebook,networkUserId:FBSDKCoreKit.AppEvents.shared.anonymousID)
Apple Search Ads
上传 Apple Search Ads 归因数据时,请参考以下示例
针对 AdServices 框架
if let attributionToken = try? AAAttribution.attributionToken() {
let attributionDetails = ["attributionToken": attributionToken]
Appflow.shared.updateAttribution(attributionDetails, source: .appleSearchAds)
}
针对 iAd 框架
ADClient.shared().requestAttributionDetails({ (attributionDetails, error) in
guard let attributionDetails = attributionDetails else {
return
}
Appflow.shared.updateAttribution(attributionDetails, source: .appleSearchAds)
})
自定义
attribute_source = custom,更新归因数据有两种方式。
方式一:开发者必须按照该参数设置的格式传入,否则将报告错误
/// The developer must pass it in according to the format set by this parameter, otherwise an error will be reported
/// - Parameter Description
/// - channel: Corresponding to `channel` on the Appflow dashboard
/// - campaign: Corresponding to `campaign` on the Appflow dashboard
/// - ad_group: Corresponding to `ad group` on the Appflow dashboard
/// - creative: Corresponding to `creative` on the Appflow dashboard
/// - click_id: Currently just saving data
/// - attributed: The app needs to pass this value explicitly. If it is false, it will be processed according to the natural amount. If it is true, the attribution information will be written according to the value of other fields,
/// If using custom for attribution processing, attributed = True will be used by default
let attribution: [String: Any] = [
"channel": "channel name",
"campaign": "campaign",
"ad_group": "ad_group",
"creative": "creative",
"click_id": "click_id",
"attributed": true
]
Appflow.shared.updateAttribution(attribution, source: .custom) { getAttributionResponse, error in
if error == nil {
print("Attribution data reported successfully")
} else {
print("Attribution data reported Failed")
}
}
方式二:如果开发者不想自行处理数据转换问题,可以使用以下方法快速设置参数,这将自动内部处理
Appflow.shared.updateCustomAttribution(channel: "channel name", campaign: "campaign name", adGroup: "adGroup name", creative: "creative name", clickID: "clickID") { getAttributionResponse, error in
if error == nil {
print("Attribution data reported successfully")
} else {
print("Attribution data reported Failed")
}
}
8. 欢迎页面
SDK 添加了 AppflowWelcomePage 视图。如果用户需要启动欢迎页面,需要在 Appflow 平台上配置页面的配置。欢迎页面的加载方法可以参考以下内容
// loading WelcomPage
func loadLaunchScreenView() {
// customized launch screen
if let window = self.window {
window.backgroundColor = .white
let welcomPageView = AppflowWelcomePage(frame: window.bounds)
welcomPageView.waitingTime = 6
self.window?.makeKeyAndVisible()
self.window?.addSubview(welcomPageView)
self.window?.bringSubviewToFront(welcomPageView)
}
}
首次加载欢迎页面可能需要一些时间。默认加载等待时间为 30 秒。用户可以根据需要设置 等待时间。
9. 应用内消息
应用内消息有两种加载方式,分为自动和手动:
a. 自动设置弹窗内的应用内消息
如果用户访问推送功能并希望自动管理和显示接收到的应用内消息,可以调用以下方法来启用此功能。应用内消息功能默认是禁用的。 示例:
//In App Message
//true: automatically displayed;
//false: not automatically displayed
Appflow.shared.setInAppMesssageAutoShow(isAuto: true)
如果设置 'setInAppMesssageAutoShow = True',则开发者可以根据需要设置应用内消息的延迟时间,默认延迟时间为1.5秒
解释:当App处于后台时,点击推送消息内容进入App。应用内消息不能立即显示。需要等待App启动后才能显示内容。在此设置过程中,需要有一定的延迟时间来加载缓冲。默认的缓冲加载延迟时间为1.5秒,可以根据自己项目的效果进行设置。示例:
//Set the delay time for message display
Appflow.shared.setFromBackgroudPointToDelayTime(delayTime: 2)
注意:方法'a':不适用于可以访问消息推送或需要处理消息内容的项目。如果消息推送功能已经连接,可以使用方法'b'来访问'应用内消息'
b. 手动设置弹窗内的应用内消息
如果项目本身已连接到消息推送功能,如果您想使用应用内消息功能,在接收到消息后,可以调用 sendUserNotificationCenterMessage(userInfo:[AnyHashable:Any]) 方法,将消息打包并发送到 SDK,SDK 会自动解析消息内容以判断是否为 '应用内消息',如果是,则自动显示并返回 true,如果不是 '应用内消息',则不会显示并返回 false。示例:
//UserInfo: Apps can set the userInfo for locally scheduled notification requests. The contents of the push payload will be set as the userInfo for remote notifications.
//The custom data to associate with the notification.
//userInfo :notification.request.content.userInfo
Appflow.shared.sendUserNotificationCenterMessage(userInfo: userInfo)