ASA簡介
蘋果ASA搜索廣告服務已全面上線,在App Store中搜索關鍵詞,搜索結果的頂部會出現帶有“廣告”標識的App展示。
ASA擁有高轉化率、低成本、用戶精准、流量安全等優勢,是一個相當重要的獲量渠道。ASA不知道是什么請看這里
https://ads.apple.com/cn/?cid=BD-BZ-Desktop-SC-CN-001
ASA投放
創建ASA賬號,以及投放廣告是BD和運營的事情,這些不清楚的請參考百度。
ASA歸因
這里是該文章的重點。
ASA的自歸因和第三方歸因主要差異是什么
ASA自歸因:App直接和廣告媒體做數據對接,並進行歸因。
第三方歸因:App通過第三方歸因平台處理數據統計業務。

ASA后台和第三方歸因的數據是實時的嗎?
ASA后台不是。正常情況下Apple Ads后台的數據有3-6個小時的延遲,有時甚至會達到24小時。
第三方歸因平台則要取決於各自的方案。通常獲取曝光和點擊數據的延時性取決於ASA的API反饋速度。
一個關鍵詞下廣告多次展示,卻沒有用戶點擊會有影響嗎?
影響ASA廣告展示的因素有三個:相關性、競價價格、用戶行為。用戶點擊屬於用戶行為的一部分,對於用戶行為表現較差的廣告,會被認為App廣告和關鍵詞的相關性並不強,從而減少廣告展示權重。
教育行業是否支持投放ASA?
目前已支持投放。近期,蘋果發布了《適用於中國大陸的Apple廣告指南》文檔的版本更新。在本次的更新內容中,就新增了“教育”類別的資質需求,可以自行查看詳情。

ASA的自歸因
我公司合作的數據統計平台如果開通第三方歸因服務每年需要8W,所有自然就是自己處理,雖然效果差些,但分析數據現階段夠了。下面進入正題
Apple Ads 歸因 API 說明
AdServices & iAd
兩個方案的版本支持及成功率
集成步驟
客戶端處理邏輯參考
歸因數據包格式說明
iOS14.3+
["adGroupId": 1234567890, "creativeSetId": 1234567890, "conversionType": Download, "orgId": 1234567890, "clickDate": 2021-12-07T04:25Z, "keywordId": 12323222, "countryOrRegion": US, "campaignId": 1234567890, "attribution": 1]
iOS14.3-XX
"Version3.1": {
"iad-adgroup-id" = 1234567890;
"iad-adgroup-name" = AdGroupName;
"iad-attribution" = true;
"iad-campaign-id" = 1234567890;
"iad-campaign-name" = CampaignName;
"iad-click-date" = "2021-12-03T07:32:38Z";
"iad-conversion-date" = "2021-12-03T07:32:38Z";
"iad-conversion-type" = Download;
"iad-country-or-region" = US;
"iad-creativeset-id" = 1234567890;
"iad-creativeset-name" = CreativeSetName;
"iad-keyword" = Keyword;
"iad-keyword-id" = 12323222;
"iad-keyword-matchtype" = Broad;
"iad-lineitem-id" = 1234567890;
"iad-lineitem-name" = LineName;
"iad-org-id" = 1234567890;
"iad-org-name" = OrgName;
"iad-purchase-date" = "2021-12-03T07:32:38Z";
}
這里我是登錄后對數據進行了二次加工,把userId和與后台定義的type加進去
iOS14.3+
["adGroupId": 1234567890, "creativeSetId": 1234567890, "conversionType": Download, "orgId": 1234567890, "clickDate": 2021-12-07T04:25Z,
"keywordId": 12323222,"userId": "20XX", "countryOrRegion": US, "campaignId": 1234567890, "type": 1, "attribution": 1]
iOS14.3-XX
["userId": "20XX";
"type": 2;
"Version3.1": {
"iad-adgroup-id" = 1234567890;
"iad-adgroup-name" = AdGroupName;
"iad-attribution" = true;
"iad-campaign-id" = 1234567890;
"iad-campaign-name" = CampaignName;
"iad-click-date" = "2021-12-03T07:32:38Z";
"iad-conversion-date" = "2021-12-03T07:32:38Z";
"iad-conversion-type" = Download;
"iad-country-or-region" = US;
"iad-creativeset-id" = 1234567890;
"iad-creativeset-name" = CreativeSetName;
"iad-keyword" = Keyword;
"iad-keyword-id" = 12323222;
"iad-keyword-matchtype" = Broad;
"iad-lineitem-id" = 1234567890;
"iad-lineitem-name" = LineName;
"iad-org-id" = 1234567890;
"iad-org-name" = OrgName;
"iad-purchase-date" = "2021-12-03T07:32:38Z";
}]
之后對數據進行轉json字符串處理然后傳給后台
高版本
低版本
字段 | 類型 | 說明 |
---|---|---|
iad-attribution | Boolean | 如果用戶在應用下載前30天點擊了Apple Search Ads廣告,則為True。 |
iad-org-name | String | 廣告系列所屬的賬戶組織名稱 |
iad-org-id | Integer | 廣告系列所屬的賬戶組織ID |
iad-campaign-id | Integer | 廣告系列ID |
iad-campaign-name | String | 廣告系列名稱 |
iad-click-date | Date/time string | 用戶點擊相應廣告的日期和時間 |
iad-purchase-date | Date/time string | 用戶首次下載您的應用的日期和時間。 當iad-conversion-type的值為“Redownload”,此字符串表示原始購買日期。 該購買可能與Apple Search廣告無關。 |
iad-conversion-date | Date/time string | 用戶通過點擊Apple搜索廣告下載您的應用的日期和時間。 |
iad-conversion-type | String | 表明是否首次下載。"Redownload"說明用戶在本設備下載/卸載過,或者用同一賬戶在其他設備下載過。 |
iad-adgroup-id | Integer | 廣告組ID |
iad-adgroup-name | String | 廣告組名稱 |
iad-country-or-region | String | 廣告系列相關的國家或地區 |
iad-keyword | String | 帶來廣告展示次數並帶來相應廣告點擊的關鍵字 |
iad-keyword-id | String | 帶來廣告展示次數的關鍵字的ID |
iad-keyword-matchtype | String | 帶來廣告展示次數的關鍵字的匹配類型。 值是廣泛匹配、完全匹配或搜索匹配。 |
iad-creativeset-id | Integer | 相應廣告所屬的廣告素材集的ID |
iad-creativeset-name | String | 相應廣告所屬的廣告素材集的名稱 |
后台存表處理邏輯參考
客戶端獲取歸因數據示意代碼
// LXADSHelper.swift // Psybot // // Created by 李俊成LX on 2021/12/3. // Copyright © 2021 lianxin. All rights reserved. // import AdSupport import AdServices import iAd import AppTrackingTransparency import Foundation import RxSwift import RxCocoa import NSObject_Rx class LXADSHelper{ static let disposeBag = DisposeBag() class func initSDK() { //蘋果ASA;延遲4秒再發送,等ATT用戶操作結果,可能有IDFA DispatchQueue.main.asyncAfter(deadline: .now() + 7) { let defaultStand = UserDefaults.standard let pushedADS = defaultStand.value(forKey: "pushedADS") if Platform.isSimulator { }else { if pushedADS == nil { self.logAds() } } } } /// 蘋果Ads廣告 /// TODO:有些舊設備新系統(iPhone8),會出現token為空的問題 class func logAds() { if #available(iOS 14.3, *) { var token: String? = nil do { token = try AAAttribution.attributionToken() } catch { } LXSimpleLogs("LogAds:AdServces,Token: \(token ?? "")") if let token = token { // 1、發送POST給蘋果得到歸因數據 sendToken(getANullableString("token", content: token)) { attrData in // 異步,會延后 LXSimpleLogs("LogAds:14.3+ Dict: \(attrData ?? [:])") // TODO::發送數據給服務端 // ... ... if attrData != nil { var attrDataL:[String:Any] = attrData! // 添加userId attrDataL["type"] = "1" let defaultStand = UserDefaults.standard defaultStand.set(attrDataL, forKey:"pushADSDic") defaultStand.synchronize() self.logOpen() } } } }else{ if ADClient.shared().responds(to: #selector(ADClient.requestAttributionDetails(_:))) { LXSimpleLogs("LogAds:iAd called") ADClient.shared().requestAttributionDetails({ attrData, error in // 異步,會延后 LXSimpleLogs("LogAds:14- Dict: \(attrData ?? [:])") // TODO::發送數據給服務端 if attrData != nil { var haveVersion :Int = 0 var haveVersionStr :String = "Version" for keystr in attrData!.keys { if keystr.contains("Version") { haveVersion = 1 haveVersionStr = keystr } } if haveVersion == 1 { var attrDataLL : [String:Any] = [:] let attrDataL:[String:Any] = attrData![haveVersionStr] as! [String : Any] // 舊數據統一處理一下 attrDataLL["iadPurchaseDate"] = attrDataL["iad-purchase-date"] attrDataLL["iadLineitemId"] = attrDataL["iad-lineitem-id"] attrDataLL["iadOrgName"] = attrDataL["iad-org-name"] attrDataLL["iadCreativesetId"] = attrDataL["iad-creativeset-id"] attrDataLL["iadCreativesetName"] = attrDataL["iad-creativeset-name"] attrDataLL["iadOrgId"] = attrDataL["iad-org-id"] attrDataLL["iadLineitemName"] = attrDataL["iad-lineitem-name"] attrDataLL["iadAdgroupName"] = attrDataL["iad-adgroup-name"] attrDataLL["iadConversionDate"] = attrDataL["iad-conversion-date"] attrDataLL["iadClickDate"] = attrDataL["iad-click-date"] attrDataLL["iadKeywordMatchtype"] = attrDataL["iad-keyword-matchtype"] attrDataLL["iadCountryOrRegion"] = attrDataL["iad-country-or-region"] attrDataLL["iadConversionType"] = attrDataL["iad-conversion-type"] attrDataLL["iadKeywordId"] = attrDataL["iad-keyword-id"] attrDataLL["iadCampaignId"] = attrDataL["iad-campaign-id"] attrDataLL["iadAttribution"] = attrDataL["iad-attribution"] attrDataLL["iadCampaignName"] = attrDataL["iad-campaign-name"] attrDataLL["iadKeyword"] = attrDataL["iad-keyword"] attrDataLL["iadAdgroupId"] = attrDataL["iad-adgroup-id"] // attrDataLL["type"] = "2" let defaultStand = UserDefaults.standard defaultStand.set(attrDataLL, forKey:"pushADSDic") defaultStand.synchronize() self.logOpen() } } }) } } } /// 讀取可能為空的字符串 class func getANullableString(_ desc: String?, content: String?) -> String? { if content == nil { return "" } return "\(content ?? "")" } /// 發送歸因token得到數據 class func sendToken(_ token: String?, completeBlock: @escaping (_ data: [String : Any]?) -> Void) { let url = "https://api-adservices.apple.com/api/v1/" var request: URLRequest? = nil if let url1 = URL(string: url) { request = URLRequest(url: url1) } request?.httpMethod = "POST" request?.addValue("text/plain", forHTTPHeaderField: "Content-Type") let postData = token?.data(using: .utf8) request?.httpBody = postData // 發出請求 URLSession.shared.dataTask(with: request!) { data, response, error in var result: [String : Any]? = nil if error != nil { // 請求失敗 LXSimpleLogs("LogAds:sendToken ERR") let nulldict: [String : Any] = [:] completeBlock(nulldict) }else{ // 請求成功 var resDic: [String : Any]? = nil do { resDic = try JSONSerialization.jsonObject(with: data!, options: []) as? [String : Any] } catch _ { } result = resDic completeBlock(result) } }.resume() } /// 激活日志,這里登錄后發送 class func logOpen() { let defaultStand = UserDefaults.standard let pushedADS = defaultStand.value(forKey: "pushedADS") if pushedADS == nil && LXUserModel.islogined , let userId = LXUserModel.localModel()?.userId{ LXSimpleLogs("LogOpen") var attrData:[String:Any] = defaultStand.value(forKey: "pushADSDic") as! [String : Any] if attrData.keys.count > 0 { // 添加userId attrData["userId"] = userId LXSimpleLogs("LogOpenAds Dict: \(attrData)") // 上傳數據 let params = ["asaData":attrData.jsonString] as [String : Any] LXSimpleLogs("LogOpenAds Dict_params: \(params)") DispatchQueue.main.async { //code 上傳數據偽代碼 // 上傳數據后OK let defaultStand = UserDefaults.standard defaultStand.set(true, forKey:"pushedADS") defaultStand.synchronize() } } } } struct Platform { static let isSimulator: Bool = { var isSim = false #if arch(i386) || arch(x86_64) isSim = true #endif return isSim }() } }
大數據分析歸因處理參考
以上就把相關的歸因數據存表了,但是有的只有關鍵詞ID,並沒有對應的關鍵詞,這里就會用到獲取對應關鍵詞的官方API接口
當然如果不想這樣,那就通過批量上傳關鍵詞,在上傳關鍵詞.csv文件的時候給大數據工程師一份。
好了,就這樣吧!
參考:https://baijiahao.baidu.com/s?id=1709681570145946947&wfr=spider&for=pc