一:前言
在對原理進行了一定的瞭解及完成證書的準備後,在APP端進行服務的註冊,deviceToken的接收和上傳,實現消息接收的回調方法等簡單的實現工作。
思考:在IOS系統中,爲保證設備在安裝衆多應用後,在系統應用體驗上還能夠提供比較友好的用戶體驗。蘋果系統在設計的時候對很多功能進行了系統級別的限制,如後臺運行能力等。就APNS而言,蘋果爲實現各方面的可控,主動建立自己的推送服務,而不任由用戶自己及不知名的第三方公司實現。在系統層上進行對所有應用的消息進行統一管理。即保證了傳輸數據時的安全,也保證了性能及實現傳輸通道的共享。
二:服務的註冊
APNS的代碼接入我直接以擴展的方式進行文件分離,裏面包含“註冊服務”,“註冊回調,“推送回調”等方法。這裏行介紹註冊相關。
- 註冊代碼
//MARK:- 初始化推送
func registerForAPNS(application: UIApplication) {
if #available(iOS 10, *) { //ios10 以上系統
let center = UNUserNotificationCenter.current()
center.delegate = self
let options: UNAuthorizationOptions = [UNAuthorizationOptions.alert, .badge, .sound]
center.requestAuthorization(options: options) { (granted: Bool, error: Error?) in
if granted == true {
print("註冊消息推送成功")
}else {
print(error ?? "註冊消息推送失敗")
}
}
}
application.registerForRemoteNotifications()//調用註冊服務,在回調方法中返回device token
}
- 註冊服務相關回調
//MARK:- UNUserNotificationCenterDelegate,deviceToke註冊成功
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let device: NSData = NSData(data: deviceToken)
var deviceId: String = device.description.trimmingCharacters(in: CharacterSet(charactersIn: "<>"))
deviceId = deviceId.replacingOccurrences(of: " ", with: "")
print("apns推送證書 -- \(deviceId)")
}
//如果deviceToken獲取不到會進入此事件
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("註冊推送失敗了 - \(error)")
}
- 註冊服務一般在應用啓動後進行調用,如下圖:
三:Token的接收及上傳
這裏恐怕就不用上代碼了,這時說一下在項目中常用的處理方式:
-
不帶用戶帳號身份的deviceToken上傳方案:
一般出現在衆多不依賴強用戶登錄使用的APP上,如新聞類,購物類APP等。但儘管如此出於業務考慮,如行爲記錄等,一般在提交deviceToken的同時,也要同時提交一個本地生成的“設備唯一號”用於我們自己的服務端對設備進行記錄跟蹤。而“設備唯一號”的生成方式現在比較受用的是能過取得本地的廣告ID加UUID,經過MD5之後,存在鑰匙庫裏。 -
帶用戶帳號身份的deviceToken上傳方案:
對於依賴強用戶登錄使用的APP,在應用打開的時候,在之前使用過的情況下會進行自動登錄調用。同時在調用“註冊APNS服務”的調用時,也是進行並行調用的。一般人的做法是自動登錄調的時候同時提交deviceToken參數,但這方案個人不建議採用。最好還是獨立一個接口用於上傳綁定deviceToken操作。
用戶在登錄後一般要對用戶信息進行全局內存緩存,可以同時對“用戶信息”及“deviceToken”進行全局內局的KVO監聽處理,當同時兩個值到存在的同時才做“上傳綁定deviceToken”操作。
另一方面,最好要對上傳成功與失敗進行全局記錄。當發生網絡切換時,從無網到有網的轉換時,再對“上傳綁定deviceToken”進行調用,這樣就更加完美了。 -
只上傳deviceToken,無其它任何附加參數:
這個就最簡單了,可以參考我上面寫的方案進行單一deviceToken上傳即可。
四:回調方法的實現
//MARK:- UNUserNotificationCenterDelegate
//程序處於前臺收到推送消息時回調
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void){
let content:UNNotificationContent = notification.request.content
print("前臺收到推送消息 = \(content.userInfo)")
completionHandler([.alert, .sound])
}
//點擊收到的apns消息推送,無論是app未打開,還是app在前臺,只要點消息框都回調
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void){
print("點擊收到的apns消息推送 => \(response.notification.request.content.userInfo)")
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) {
print("openSettingsFor")
}
看上面註釋應該已經很清楚了,同時這時值得注意的是completionHandler方法的調用。
-
有前臺接收推送的回調方法中,
completionHandler
方法必須在方法的最後調用一次,給系統提供該應用在前臺狀態下當接收到推送消息時的操作選項參數。如顯示提醒彈框.alert
,播放聲音.sound
,顯示角標.badge
。在completionHandler
方法的調用時,必須傳入UNNotificationPresentationOptions
類型的數組
作爲參數。 -
在應用打開狀態下,應用收到推送消息,如果想交由程序進行處理的話,如合成聲音進行播放,自定義應用內消息彈出界面等需求,則可以對completionHandler傳入空數組
completionHandler([])
。當有前臺狀態下接收到APNS推送服務發來的消息時,不彈出消息提醒提示框
,同時沒有聲音提醒及badge
數字的變化。 -
在非前臺狀態下(打開APP進入後臺 或者 APP未打開)接收推送的回調方法中,
completionHandler
方法也同樣要在方法的最後調用一次,但不傳入任何參數。系統對於該狀態下的操作選項參數
默認爲completionHandler([.alert, .sound, .badge])
並不能修改。此時,當接收到推送消息時,彈出提示框,播放聲音(當payload中有指定 sound值時),應用圖標中顯示角標(當payload中有指定badge值時) -
同時,無論應用是什麼狀態下,只要是點擊推送消息,都會回調didReceive response回調方法。與是否打開應用及前後臺無關。
上面只提供了IOS10版本及以後的代碼實現。以兼容三個版本的原則,感覺已經足夠。
五:測試效果
以Demo及以上代碼爲例,實現完成後我們可以藉助一些調用APNS服務的工具進行測試,如Easy APNS Provider爲例:
操作步驟如下:
- 打開應用,獲取deviceToken。(細心的朋友會看到,在代碼中,當deviceToken返回的時候,我們對於返回的值進行了空格等字符的替換)
- 打開Easy APNS Provider,添加deviceToken。
- 選擇證書文件(就是我們上一篇文章上生成並下載的推送證書aps_development.cer文件)。
- 選擇APNS測試服務(sandbox.push) ,點擊“連接至”。
- 構建PayLoad(如上圖),點擊發送推送。手機端即可收到消息並顯示。
六:DeviceToken解綁
有上傳DeviceToken綁定就會有DeviceToken解綁的時候,一般只會出現在用戶對APP進行主動退出登錄操作的時候。
一般服務端會提供一個“退出登錄”的接口,其中就包括冊除自己服務端用戶表中的DeviceToken的值操作。
在項目中,如“退出登錄”操作,一般可通過建立離線提交服務進行離線操作。一方面把操作交由“離線提交系統”處理,同是清空用記內存,馬上退回到登錄頁面,在用戶體驗上更友好。
七:aps推送參數
在payload中的aps參數中,提供了對推送相關的顯示,聲音播放,角標控制等參數相關的屬性參數。相關參數的簡單描述如下:
alert
爲推送提供內容描術等相關顯示數據。其中title
表示標題
,body
表示推送消息內容
。在實際的測試中發現,title與body其中一者有值時推送彈框都會彈出
,當都沒有值時,不顯示彈框,但推送是有接收的。badge
用於設置應用圖標上的角標,如"badge" : 1
。在實際的測試中發現,當前一次接收的通知有設置值,後一接收的通知沒有設置該值時,原來角標值不變。同上產面提到,在前臺狀態下,當completionHandler
方法調用時的參數中沒有設置.badge
時,badge屬性不生效。sound
用於設置推關收到後的提示音,如"sound" : "default"
。需要注意的是,當提示音沒有指定後綴時,系統自動查找對應的系統單效,當不存時,指定默認的系統提示音來播放。當例如"sound" : "hello.mp3"
,時系統到應用目錄查找是否存在該聲音文件,存在是播放(當文件格式與大小符合規範時),查找失敗時,也會指定默認的系統提示音來播放。content-available
用於標識推送方式是否是靜默推送
,如"content-available" : 1
。靜默推送後面想獨立講一下。