遠程推播送達與點擊的計算
遠程推播是一個相當重要的工具,能夠讓開發者在使用者未打開 App 時,主動在使用者的裝置推播訊息並導流,因此好的推播內容與適當的頻率,可顯著提升使用者的活躍度。
而推播的 Payload 型態可包含一般推播與靜默推播(Silent Push),關於這兩個推播的分別,網路上各式講法不一,因此花了點時間佈置測試,並就測試結果整理成應用上須注意的事項,如果有錯誤的地方或偏誤的概念,歡迎指正。
網路上有相當多設置推播的技術文章,因此此處不詳談,主要就使用者與推播的互動,對於營運上的需求進行整理。
在營運上常設置Funnel,從使用者進入到預期的成果,在可能的環節做統計,藉此來評估成效與優化,而在推播的過程中,可設置幾個階段來評估成效。
其中可看到,營運上需要知道成功送達率以及點擊率,來做監測與優化,以下就”用戶給予的權限對推播的影響”與”程式收到的 Receive Notification”,這兩個維度來測試。
測試環境
Xcode Version:13.2.1
Device iOS Version:15.5
推播與權限
Q. 靜默推播可以跳出彈窗與發出聲音嗎?
A. 可以,只要使用者允許推播權限並在payload設置alert。
{
"aps": {
"sound": "",
"content-available": 1, //Silent Push
"alert": {
"title": "通知-急事",
"subtitle" : "好戲上場",
"body" : "線上影視"
}
},
"custom_key": {
"custom_key_1": "custom_value_1",
"custom_key_2": "custom_value_2"
}
}
Q. 如何跳出推播權限請求彈窗?
A. 在適當的時機 call function,並注意當使用者回答過後,此彈窗重複 call也不會跳出,只能導使用者去設定開啟,因此適當的詢問時機是要緊的。
UNUserNotificationCenter.current().requestAuthorization(options:
[.alert,.sound,.badge]) { granted, error in
}
Q. 從來沒有跳出權限要求彈窗會有什麼影響?
A. 使用者無法在設置看到推播權限的開關,並且也無法跳出推播內容。
Q. 若使用者不允許推播,拿得到 Device Token 嗎?
A. 可以,不影響。
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceTokenString = deviceToken.map { String(format: "%02x", $0) }.joined()
NSLog("***Token:\(deviceTokenString)")
}
Q. 使用者不允許推播,會影響通知送達嗎?
A. 此權限僅是無法彈出 Alerts, sounds, icon badges 這些互動上的顯示,但靜默推播(Slient Push)仍可推到使用者裝置,從而啟動 App 做背景更新。
Q. 使用者將 Background App Refresh 關閉會影響什麼?
A. 這會影響 Silent Push 是否能送達,若使用者關閉代表此 App 無法在背景喚起,因此 Silent Push 也無法喚起 App。
*例外:當使用者允許權限通知,卻關閉 Background App Refresh,在此狀況下,權限的優先度比較高,因此仍可以正常收到推播與喚起 App。
Q. App在背景喚起時,可以活躍多久?
A. 活躍的時間約為 30 秒。
Q. 若使用者當下正在使用 App,推播送達時是否會跳出彈窗?
A. 以設計原則來說,當使用者已經在使用 App 時,預設是不會收到推播內容打擾用戶,也不會躺在通知中心,不過 App 依然會觸發 Recieve Notification。
Q. 繼上題,能否讓使用者當下正在使用 App 時,仍能跳出推播內容?
A. 可以,需要接 UNUserNotificationCenter.current().delegate = self,並在willPresent notification 中,送出completionHandler(UNNotificationPresentationOptions.alert)。
UNUserNotificationCenter.current().delegate = self
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification, withCompletionHandler
completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
//只要有下complete,即使在使用App中,也可以跳出UI
completionHandler(UNNotificationPresentationOptions.alert)
}
}
推播送達率與點擊率
當使用者接收到通知或點擊時,系統預設會觸發didReceiveRemoteNotification
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
//如果沒有接 UNUserNotificationCenter.current().delegate = self,則會全部觸發此事件
//Normal-Receive(使用中,不會跳出UI、通知中心也沒有)
//Silent(UI)-Receive(使用中/背景/手動滑掉App,不會跳出UI、通知中心也沒有)
//Normal-Click
//Silent(UI)-Click
NSLog("**didReceiveRemoteNotification")
completionHandler(UIBackgroundFetchResult.newData)
}
使用此預設的壞處是會將 receive 與 click 的行為混在一起,使開發者無法區別計算,也沒辦法在前景跳出推播,因此會傾向接 UNUserNotificationCenter.current().delegate = self,能有更多彈性。
但要特別注意的是,在 iOS 9 以前,無論是一般通知還是靜默通知(Silent Push)都是觸發 didReceiveRemoteNotification,無法區分 Receive 與 Click,因此接下來的計算只適用於 iOS 10 以上版本。
接 UNUserNotificationCenter.current().delegate = self,會觸發的 functions 如下:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
//接UNUserNotificationCenter.current().delegate = self
//Silent Push-Receive(使用中/背景)、Normal Push-Receive(背景)會觸發
let aps = userInfo["aps"] as? [String:Any]
if aps?["content-available"] != nil {
//Silent Push
NSLog("***Silent Push-Receive(使用中/背景)")
} else {
//Normal Push
NSLog("***Normal Push-Receive(背景)")
}
if UIApplication.shared.applicationState == .active {
NSLog("***App在使用中")
}
UNUserNotificationCenter.current().getNotificationSettings { setting in
let status = setting.authorizationStatus
NSLog("***是否同意通知權限:\(status == .authorized)")
}
completionHandler(UIBackgroundFetchResult.newData)
}
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
//接UNUserNotificationCenter.current().delegate = self
//Normal-Receive(前景)、Silent-Receive(前景)會觸發
let aps = notification.request.content.userInfo["aps"] as? [String: Any]
if let silentNotiKey = aps?["content-available"] {
//Silent Push
NSLog("***Silent Push-Receive(使用中)")
} else {
NSLog("***Normal Push-Receive(使用中)")
}
UNUserNotificationCenter.current().getNotificationSettings { setting in
let status = setting.authorizationStatus
NSLog("***是否同意通知權限:\(status == .authorized)")
}
//只要有下complete,即使在使用App中,也可以跳出UI
completionHandler(UNNotificationPresentationOptions.alert)
}
}
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
//Normal-Click、Silent-Click會觸發
let aps = response.notification.request.content.userInfo["aps"] as? [String: Any]
if let silentNotiKey = aps?["content-available"] {
//Silent Push
NSLog("***Silent Push-Click")
} else {
NSLog("***Normal Push-Click")
}
completionHandler()
}
}
將上述觸發行為整理後,列出以下表格,縱欄為通知送達或點擊,橫列為 App 當下的狀態。
一般通知
可以發現在一般通知下,無法準確計算送達率。因為當 App 沒被使用而通知送達時,App 並不會觸發任何 receive 的通知,明顯不符合推播的情境,因此藉由一般通知來計算送達的成功率,是不可行的,而 Click 則可以被順利計算。
靜默推播
若想要計算送達率,可以考慮使用靜默通知帶 Alert 的方式,但要記得靜默通知的設計目的並不是為了彈出通知,而是用於在後台將 App 與 server 的資料同步。因此此通知在 APNs 的優先順序低,不保證一定送達,且頻率無法過高(每小時不超過 2~3 次)。在保證低頻率且需要知道送達率的狀況下,使用此機制進行推播。
計算送達率
在計算時有以下情況需要考慮
1. 若使用者不同意推播權限,靜默通知送達時仍會收到通知,這時需考慮是否將此次送達納入計算,因為使用者並無法看到推播內容。
UNUserNotificationCenter.current().getNotificationSettings { setting in
let status = setting.authorizationStatus
NSLog("***是否同意通知權限:\(status == .authorized)")
}
2. 當使用者正在使用 App 時,預設不會跳出推播內容,但也可以用接 delegate 的方式,讓推播內容照常跳出。但無論內容跳出與否,都會收到送達通知(willPresentNotification/didReceiveRemoteNotification),這時依需求決定是否將此次送達納入計算。
3. 當使用者正在使用 App 時,推播送達時會觸發兩個 functions(willPresentNotification/didReceiveRemoteNotification),注意判斷 App 的當前狀態與推播參數來避免重複計算。
willPresentNotification 需帶有 alert、badge、sound 等參數時才會觸發,若沒有 UI 的變化,則只會觸發 didReceiveRemoteNotification。
4. 依照 Apple 的設計,系統可能會暫停並延遲通知的傳送,此時會有幾個副作用:
*系統只會持有一個通知,新的會替代舊的,意即此階段不管送幾則通知, 皆只會觸發一次 didReceiveRemoteNotification
* App 強制退出或終止時(例如使用者手動關閉),系統便會丟棄保留中的通知,也就是不會觸發 didReceiveRemoteNotification
*使用者開啟 App 時,系統會馬上發送保留的通知,觸發一次 didReceiveRemoteNotification
當遇到上述狀況時,推播的內容彈窗依然會跳出,使用者可收到推播內容,但不一定會觸發 didReceiveRemoteNotification。
針對這個問題,在測試時,只能就「用戶手動強制關閉 App」 的狀況下做測試,發現較容易遇到送達當下沒有收到 didReceiveRemoteNotification,遞延至用戶開啟 App 時觸發,但也可能遇到沒有遞延,正常觸發的時候。
因此機制屬於 Apple 系統自控的部分,並沒有任何方式處理,因此在計算送達率時,將這個變因記下來,因送達率是個相對比較的關係,不一定需要絕對準確值,因此此變因先忽略不計。
計算點擊率
因點擊時會打開 App,因此無論是什麼類型的通知都可以正確觸發 didReceiveNotificationResponse,只需直接計算即可。
結論
靜默通知依然可以跳出推播彈窗,但跳出與否仍需用戶給予權限,但這並不影響靜默通知在背景喚醒 App,收到 response 的行為。
在計算送達時,需注意用戶是否有允許通知、是否要讓用戶在使用 App 時仍能看到推播內容、App 在使用中時是否會重複計算送達數等這些因素。