版權(quán)聲明 本文翻譯自:raywenderlich.com 原文作者: Jack Wu 譯者: JMStack 轉(zhuǎn)載請(qǐng)說明原文及譯文出處.
iOS開發(fā)者們喜歡想象他的用戶們會(huì)每時(shí)每刻使用他們所開發(fā)的APP,但是殘酷的事實(shí)是他們的用戶會(huì)有關(guān)閉APP來處理其它事情的時(shí)候.就像你洗好的衣服總要人去疊吧.
幸好,推送通知功能可以讓開發(fā)者與用戶建立連接并進(jìn)行簡(jiǎn)單的交互即使用戶當(dāng)前并沒有使用APP!
從推送通知功能第一次問世到現(xiàn)在已經(jīng)變得越來越強(qiáng)大.在iOS9上,遠(yuǎn)程推送可以做到:
- 顯示短文本
- 播放通知提示音
- 設(shè)置APP圖標(biāo)的角標(biāo)
- 在不打開APP的情況下,允許用戶與APP交互
- 允許APP在后臺(tái)靜默喚醒來執(zhí)行任務(wù)
這份遠(yuǎn)程推送通知教程會(huì)告訴你遠(yuǎn)程推送的工作原理的并讓你了解它的一些特性.
在開始推送測(cè)試之前你需要具備以下條件:
- 一臺(tái)iOS設(shè)備.遠(yuǎn)程推送不能在摸擬器上運(yùn)行,所以你需要一臺(tái)真機(jī).
- 一個(gè)開發(fā)者帳號(hào) 從Xcode7開始,在真機(jī)上測(cè)試APP不再需要加入開發(fā)者計(jì)劃.但是為了配置遠(yuǎn)程推送,你需要有一個(gè)與APP ID對(duì)應(yīng)的推送證書,獲得這個(gè)證書你需要加開發(fā)者計(jì)劃.
開始
為了接收發(fā)送遠(yuǎn)程推送通知你必須完成以下3個(gè)主要的任務(wù):
- app必須正確配置并注冊(cè)APNS(Apple Push Notification Service),以便所有設(shè)置都完成時(shí)就能馬上接收到通知
- 服務(wù)端必須向APNS發(fā)送一條明確指向一個(gè)或多個(gè)設(shè)備的通知
- app必需接收服務(wù)端發(fā)送的通知;app可以執(zhí)行通知包含的任務(wù)或者在application的代理(delegate)回調(diào)方法內(nèi)處理用戶交互行為.
任務(wù)1和任務(wù)3是這份推送通知教程主要關(guān)注的內(nèi)容,因?yàn)檫@兩個(gè)任務(wù)是iOS開發(fā)者的工作.
任務(wù)2也會(huì)在這份教程中簡(jiǎn)略的提及,并且多數(shù)情況僅僅是為了測(cè)試目的.發(fā)送一個(gè)遠(yuǎn)程通知是app服務(wù)端的工作,并且這部分內(nèi)部會(huì)因?yàn)锳pp的不同而不同.大多數(shù)app都會(huì)使用第三方服務(wù)(比如Parse.com或者Google ColoudMessaging)推送通知,其它的app或使用定制化的解決方案或使用比較流行的框架(比如: Houston).
正式開始之,下載已經(jīng)準(zhǔn)備好的 WenderCast 開始工程.WenderCast是一個(gè)讓用戶獲取raywenderlich.com播客節(jié)目和時(shí)實(shí)消息的應(yīng)用.
在Xcode中打開WenderCast.xcodeproj簡(jiǎn)單瀏覽一下.編繹運(yùn)行即可查看當(dāng)前最新播客節(jié)目:
這個(gè)app的存在的問題是當(dāng)有新的播客節(jié)目可以獲取時(shí)不能通知到用戶.并且也不能顯示任何最新的消息.接下來你將用遠(yuǎn)程推送功能修復(fù)這個(gè)問題!
為App配置遠(yuǎn)程推送功能
推送通知需要較高的安全性.這點(diǎn)是非常重要的,因?yàn)槟悴粫?huì)想讓其它人給你的用戶發(fā)送通知.這也就意味著要實(shí)現(xiàn)遠(yuǎn)程推送功能你必需跳過一些坑.
打開遠(yuǎn)程推送服務(wù)
第一步是更改App ID.在Xcode中進(jìn)入 App Settings -> General 把 Bundle Identifier 改為任意唯一的字符串.
接下來你需要在你的開發(fā)者帳號(hào)下添加打開了推送通知功能的App ID.幸運(yùn)的是,Xcode有更簡(jiǎn)單的方法實(shí)現(xiàn)這個(gè)步驟.進(jìn)入 App Settings -> Capabilities 把Push Notifications設(shè)置為 On.
在Xcode完成一些下載后,看起應(yīng)該會(huì)是下面的樣子
這個(gè)步驟背后的操作是: 如果你當(dāng)前的開發(fā)者帳號(hào)下沒有對(duì)應(yīng)的App ID就會(huì)主動(dòng)創(chuàng)建App ID,并且打開推送通知功能.你可以登陸開發(fā)者中心確認(rèn)是否打開了這個(gè)功能:
如果這個(gè)過程中出現(xiàn)問題,可以手動(dòng)創(chuàng)建App ID或者點(diǎn)擊開發(fā)者中心 + 或 Edit 按鈕開啟推送通知功能.
以上就是你目前需要的配置.
注冊(cè)遠(yuǎn)程推送
注冊(cè)遠(yuǎn)程推送需要兩步.第一步,你必需向用戶請(qǐng)求推送通知許可,獲得許可之后才能注冊(cè)遠(yuǎn)程推送.如果所有步驟進(jìn)行順利,系統(tǒng)將會(huì)向你提供一個(gè) device token ,你可以把它認(rèn)為是當(dāng)前設(shè)備的"地址".
在WenderCast應(yīng)用中你需要用在應(yīng)用啟動(dòng)后立即注冊(cè)遠(yuǎn)程推送.
打開AppDelegate.swift,添加以下代碼到AppDelegate末尾.
func registerForPushNotifications(application: UIApplication) {
let notificationSettings = UIUserNotificationSettings(
forTypes: [.Badge, .Sound, .Alert], categories: nil)
application.registerUserNotificationSettings(notificationSettings)
}
這個(gè)方法創(chuàng)建了一個(gè) UIUserNotificationSettings 實(shí)例對(duì)象并把它作為參數(shù)傳給 registerUserNotificationSettings(_:) .
UIUserNotificationSettings 存儲(chǔ)你的應(yīng)用將到用到的通知類型設(shè)置.對(duì)于UIUserNotificationType的值你可以用下面幾個(gè)枚舉值的任意組合.
- .Badge 允許App在圖標(biāo)上顯示角標(biāo)數(shù)字
- .Sound 允許App播放聲音
- .Alert 允許App顯示文本
UIUserNotificationCategory 是Set類型參數(shù)當(dāng)前暫時(shí)傳 nil,以允許你指定你的app能夠處理的不同類型的通知.當(dāng)你需要實(shí)現(xiàn)可交互的通知時(shí),這樣的設(shè)置是必需的.后面的部分你將會(huì)用到可交互通知.
在 application(_:didFinishLaunchingWithOptions:launchOptions:): 方法內(nèi)的第一行調(diào)用 registerForPushNotifications(_:) :
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
registerForPushNotifications(application)
//...
}
編繹運(yùn)行.當(dāng)App啟動(dòng)時(shí)你會(huì)收到一個(gè)彈窗請(qǐng)求通知許可:
點(diǎn)擊 OK ,現(xiàn)在App可以顯示通知了.但是,如果用戶拒絕了發(fā)送通知的請(qǐng)求該應(yīng)怎么辦?
當(dāng)用戶接受或拒絕請(qǐng)求許可又或者之前做出過是否允許的選擇, UIApplicationDelegate 的一個(gè)代理方法將會(huì)被調(diào)用. 添加以下代碼到 AppDelegate :
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
}
在這個(gè)方法中,你會(huì)接收到另一個(gè) UIUserNotificationSettings 實(shí)現(xiàn)對(duì)象.這個(gè)實(shí)例對(duì)象與之前你之前所傳入的不同.你之前傳入的是你所希望的設(shè)置,而當(dāng)前這個(gè)是用戶當(dāng)前授權(quán)的設(shè)置.
在App每次啟動(dòng)時(shí)都調(diào)用 registerUserNotificationSettings(_:) 是相當(dāng)重要的.因?yàn)橛脩粼谌魏螘r(shí)候都有可能在設(shè)置應(yīng)用內(nèi)改變通知的授權(quán)許可. application(_:didRegisterUserNotificationSettings:) 方法會(huì)告訴你用戶當(dāng)前給你的App什么樣的授權(quán)許可.
現(xiàn)在第一步已經(jīng)完成,你可以注冊(cè)遠(yuǎn)程推送通知了.這一步相當(dāng)簡(jiǎn)單,因?yàn)槟悴辉傩枰蛴脩粽?qǐng)求什么許可了.用下面的代碼更新 application(_:didRegisterUserNotificationSettings:) 方法:
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
if notificationSettings.types != .None {
application.registerForRemoteNotifications()
}
}
在上面的方法中,首先檢查當(dāng)前用戶是否允許通知,如果允許直接調(diào)用 registerForRemoteNotifications().
其次, registerForRemoteNotifications() 的請(qǐng)求注冊(cè)的返回狀態(tài)會(huì)通過 UIApplicationDelegate協(xié)議中的某些方法通知你.
添加以下代碼到 AppDelegate :
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
let tokenChars = UnsafePointer<CChar>(deviceToken.bytes)
var tokenString = ""
for i in 0..<deviceToken.length {
tokenString += String(format: "%02.2hhx", arguments: [tokenChars[i]])
}
print("Device Token:", tokenString)
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
print("Failed to register:", error)
}
就像方法名所暗示的那樣,當(dāng)注冊(cè)通知成功后系統(tǒng)會(huì)調(diào)用 application(_:didRegisterForRemoteNotificationsWithDeviceToken:) 方法,否則將會(huì)調(diào)用 application(_:didFailToRegisterForRemoteNotificationsWithError:). 方法.
當(dāng)前 application(_:didRegisterForRemoteNotificationsWithDeviceToken:) 方法的實(shí)現(xiàn)看起來難以理解,其實(shí)它僅僅只是獲取 deviceToken 然后轉(zhuǎn)換成字符串.deviceToken的值就是這個(gè)過程得到的結(jié)果.它是由APNs服務(wù)器提供用來標(biāo)識(shí)當(dāng)前設(shè)備當(dāng)前App.當(dāng)發(fā)送時(shí)推送通知的時(shí)候,App用deviceToken作為"地址"傳遞到當(dāng)前設(shè)備.
注意 會(huì)有很多原因?qū)е伦?cè)失敗.最常碰到原因是程序運(yùn)行在模擬器上,或者App ID設(shè)置不正確.具體原因打印error值會(huì)提供更加詳細(xì)的信息.
到此,編繹運(yùn)行.確保你當(dāng)前運(yùn)行在真機(jī)上,你將會(huì)在控制臺(tái)看到打印出的device token.下面將會(huì)是你看到的結(jié)果:
把device token復(fù)制到某處保存.
在正式發(fā)送通知之前你還需要配置一點(diǎn)點(diǎn),所以回到開發(fā)者中心.
創(chuàng)建一個(gè)SSL證書和PEM文件
在開發(fā)者中心進(jìn)入 Certificates, Identifiers & Profiles -> Identifiers -> App IDs 找到你應(yīng)用的App ID.在 Application Services 下面 Push Notifications 應(yīng)該為 Configurable :
點(diǎn)擊 Edit 滾動(dòng)到 Push Notifications :
在 Development SSL Certificate 欄下,點(diǎn)擊 Create Certificate… 接下來的步驟就是創(chuàng)建 CSR 文件.創(chuàng)建好CSR文件后點(diǎn)擊 continue 和 Generate,這步會(huì)用你創(chuàng)建的CSR文件生成證書.最后下載并運(yùn)行生成好的證書,證書將被添加到你的鑰匙串應(yīng)用中,并與私鑰成對(duì).
在開發(fā)者中心,你的App ID現(xiàn)在推送通知功能在development下應(yīng)該處于Enable狀態(tài).
在關(guān)閉鑰匙串應(yīng)用前還有最后一件事,右擊你剛才添加的證書,選擇 Export :
保存在桌面并命名為WenderCastPush.p12.
你會(huì)被提示要求為你的.p12文件設(shè)置密碼,你可以選擇不輸或者輸入一個(gè)你想設(shè)置的密碼.這里我用"WenderCastPush"作為密碼.接下來你需要輸入電腦登陸密碼來允許導(dǎo)出p12文件.
接下來,打開你的終端并執(zhí)行以下命令來從p12文件生成PEM文件:
$ cd ~/Desktop
$ openssl pkcs12 -in WenderCastPush.p12 -out WenderCastPush.pem -nodes -clcerts
如果你導(dǎo)出p12文件時(shí)輸入了密碼,在這里你必須輸入相同的密碼.
到此為止,你已經(jīng)艱難的躍過了很多坑,這一切都是值得的.接下來你將用你生成的WenderCastPush.pem文件發(fā)送第一個(gè)通知.
發(fā)送通知
之前下載的開始工程會(huì)包含一個(gè)WenderCastPush文件;里面包含兩個(gè)用于發(fā)送通知簡(jiǎn)單腳本.你需要用到的是newspush.php.正如文件名所暗示的,這個(gè)腳本將會(huì)向你的用戶發(fā)送一個(gè)彈窗通知消息.
發(fā)送推送通知需要和APNS建立SSL連接,SSL連接是用之前創(chuàng)建的證書進(jìn)行加密.這就是為什么要生成 WenderCastPush.pem 文件.重命名 WenderCastPush.pem 為 ck.pem,并且替換掉當(dāng)前已經(jīng)存在于 WenderCastPush 文件夾下的 ck.pem 文件.
打開 newspush.php 并更新之前接收到的 $deviceToken 和導(dǎo)出文件時(shí)輸入的密碼 $passphrase
// Put your device token here (without spaces):
$deviceToken = '43e798c31a282d129a34d84472bbdd7632562ff0732b58a85a27c5d9fdf59b69';
// Put your private key's passphrase here:
$passphrase = 'WenderCastPush';
打開終端, cd 到 newspush.php 所在的文件夾,輸入:
ush.php 'Breaking News' 'https://raywenderlich.com'
如果進(jìn)行順利,你的終端將會(huì)顯示:
Connected to APNS
Message successfully delivered
現(xiàn)在,你應(yīng)該會(huì)收到你的第一條通知:
注意 如果你的App被打開并處于前臺(tái)運(yùn)行狀態(tài),你將看不到任何東西.通知已經(jīng)被投送但是App還不會(huì)處理這個(gè)通知.你只需要簡(jiǎn)單的關(guān)閉App并重新發(fā)送通知即可.
常見問題
也許你會(huì)遇到以下問題:
只能接收到部分通知:如果你同時(shí)發(fā)送多個(gè)通知,只有部分通知將會(huì)被接收,不用擔(dān)心!這正是我們想要的結(jié)果.當(dāng)發(fā)送通知時(shí)APNS會(huì)為每一個(gè)開啟了推送通知的設(shè)備保持一個(gè)高質(zhì)量服務(wù)(Quality of Service)隊(duì)列.這個(gè)隊(duì)列的大小是1,所以如果你同時(shí)發(fā)送多個(gè)通知,最后一個(gè)通知才會(huì)被發(fā)送.
連接到APNS出現(xiàn)問題:出現(xiàn)這個(gè)問題的原因可能是你的防火墻阻塞了APNS所使用的端口.所以確保你的防火墻沒有阻塞住這些端口.另一個(gè)可能的原因是私鑰和CSR文件不正確.記住,每一個(gè)App ID有一個(gè)唯一的CSR和配對(duì)的私鑰.
解剖推送通知的基本原理
在進(jìn)行任務(wù)3之前,需要理解一下你推送的通知,打開 newspush.php 文件理解發(fā)送一個(gè)通知的基本概念應(yīng)該是怎么樣的.
注意第32-40行,這就是用JSON格式編碼的裝載體.這就是實(shí)際上發(fā)送給APNS的東西.在我們當(dāng)前的例子中,裝載體像下面一樣:
{
"aps":
{
"alert": "Breaking News!",
"sound": "default"
"link_url" : "https://raywenderlich.com,
}
}
對(duì)于一個(gè)不懂JSON數(shù)據(jù)的人來說,用{}括起來的塊相當(dāng)于一個(gè)字典類型的數(shù)據(jù).
這個(gè)裝載體是一個(gè)至少包含一項(xiàng)內(nèi)容的字典,這項(xiàng)內(nèi)容就是 aps, 它本身也是一個(gè)字典.在這個(gè)例子中"aps"包含"alert","sound"和"link_url"等字段.當(dāng)接收到一個(gè)通知,就會(huì)顯示一個(gè)包含"Breaking News!"文本的提醒視圖,并且有標(biāo)準(zhǔn)的提醒音效.
"link_url"實(shí)際上是一個(gè)自定義的字段.你可以添加類似的自定義字段到裝載體中,并且它會(huì)被投送到你的應(yīng)用.因?yàn)槟悴]有在應(yīng)用中處理這個(gè)字段,所以當(dāng)前接收到這個(gè)鍵值對(duì)會(huì)什么都不做.
你可以在aps字典中添加以下5個(gè)鍵(key):
alert. 這個(gè)字段可以是一個(gè)字符串,就像當(dāng)前的例子.或是是一個(gè)字典.如果是一個(gè)字典,可以是本地化的文本或者通知的其它部分.查看蘋果文檔所支持的key.
badge. 這是一個(gè)將被顯示在應(yīng)用圖標(biāo)上的數(shù)字.你可以設(shè)置這個(gè)鍵為0來清除角標(biāo).
sound. 通過設(shè)置這個(gè)建,你可以播放存放在App本地定制的通知提示音來取代系統(tǒng)默認(rèn)的通知提示音.定制的通知提示音必須在30秒以內(nèi)并且還有一些其它的限制,你可以查看蘋果文檔了解更詳細(xì)信息.
content-available.設(shè)置這個(gè)鍵為1,當(dāng)前通知會(huì)變成靜默通知.這個(gè)部分會(huì)在這份教程的后面部分探索.
category. 這個(gè)鍵定義了通知的分類,用于顯示定制通知所包含的交互行為.同樣,接下來會(huì)探索這部分的內(nèi)容.
除此之外,你可以添加任意你想要添加的定制化數(shù)據(jù),只要裝載體不超過4096個(gè)字節(jié).
如果你玩夠了推送通知,接下來我們進(jìn)入到下一個(gè)章節(jié).
處理接收到的通知
在這個(gè)章節(jié),你將會(huì)學(xué)習(xí)當(dāng)App接收到通知后或者用戶點(diǎn)擊了通知應(yīng)該如何執(zhí)行什么樣的操作.
當(dāng)你接收到一個(gè)通知后會(huì)發(fā)生什么
當(dāng)你的app接收到一個(gè)通知, UIApplicationDelegate 的一個(gè)方法將會(huì)被調(diào)用.
需要根據(jù)接到收通知時(shí)App所處的狀態(tài)的進(jìn)行不同的處理.
如果你的應(yīng)用當(dāng)前不在運(yùn)行,并且用戶通過點(diǎn)擊推送通知啟動(dòng)應(yīng)用,通知內(nèi)容會(huì)通過 application(_:didFinishLaunchingWithOptions:) 方法的 launchOptions 參數(shù)進(jìn)行傳遞.
如果你應(yīng)用當(dāng)前正運(yùn)行在前臺(tái),推送通知將不會(huì)被顯示.但是 application(_:didReceiveRemoteNotification:) 會(huì)被立即調(diào)用.
如果你的應(yīng)用正在運(yùn)行,或者被掛起在后臺(tái),并且用戶通過點(diǎn)擊通知使應(yīng)用進(jìn)入前臺(tái) application(_:didReceiveRemoteNotification:) 方法會(huì)被調(diào)用.
在第一種情況下, WenderCast將到創(chuàng)建一個(gè)新的section,并直接打開以顯示到這個(gè)新建section.添加以下代碼到 application(_:didFinishLaunchingWithOptions:) 的末尾return語句之前.
// Check if launched from notification
// 1
if let notification = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as? [String: AnyObject] {
// 2
let aps = notification["aps"] as! [String: AnyObject]
createNewNewsItem(aps)
// 3
(window?.rootViewController as? UITabBarController)?.selectedIndex = 1
}
這段代碼做了以下3件事:
檢查 UIApplicationLaunchOptionsRemoteNotificationKey 鍵對(duì)應(yīng)的值是否存在,如果存在,這個(gè)值應(yīng)該就是你發(fā)送的通知裝載體.
如果存在,獲取 aps 對(duì)應(yīng)字典并傳給 createNewNewsItem(_:) 方法,這個(gè)方法根據(jù)接收的字典創(chuàng)建一個(gè) NewItem,并刷新表格.
改變tab控制器當(dāng)前選中的tab索引值為1,也就是直接顯示新聞控制器視圖.
為了測(cè)試這部分代碼,你需要編輯WenderCast的scheme:
在 Run -> Info 下選擇 Wait for executable to be launched:
這個(gè)選項(xiàng)會(huì)使調(diào)試器等待應(yīng)用程序安裝直到應(yīng)用程序第一次被啟動(dòng)玩焰。
編繹運(yùn)行,完成安裝后,發(fā)送一些新的動(dòng)態(tài).點(diǎn)擊通知以啟動(dòng)App,啟動(dòng)之后app會(huì)顯示一些新消息.
注意 如果你突然接收不到通知,最有可能的原因是device token被改了.如果你刪除應(yīng)用再重新安裝就有可能出現(xiàn)這種情況. 確保你的device token是正確的.
為了處理另外兩種情況,添加以下代碼到 AppDelegate:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let aps = userInfo["aps"] as! [String: AnyObject]
createNewNewsItem(aps)
}
這個(gè)方法直接創(chuàng)建了一個(gè)新的 NewsItem. 現(xiàn)在你可以把scheme設(shè)置回自動(dòng)啟動(dòng)App.
編繹運(yùn)行.保持App運(yùn)行在前臺(tái),并選中新聞頁.發(fā)送一個(gè)通知,你可以看到消息奇跡般的顯示在視線內(nèi).
就是這樣!你的App現(xiàn)在可以處理基本的推送消息.
一些需要注意的事情:很多情況推送通知可能會(huì)被遺漏.對(duì)于WenderCast應(yīng)用來說是沒有問題的,因?yàn)檠b滿消自己的列表對(duì)這個(gè)應(yīng)用來說并不是那么重要,但是一般來講你不應(yīng)該把推送通知做為傳遞內(nèi)容的唯一方式.作為備選項(xiàng),推送通知應(yīng)該僅僅只是指示當(dāng)前有新的內(nèi)容可以獲取并讓App從服務(wù)器下載這些新的內(nèi)容.WenderCast應(yīng)用在這方有一些局限性,因?yàn)樗]有合適的服務(wù)端.
可交互的通知
可交互的通知允許你添加定制化的按鈕在通知上.你也許注意到郵件通知或者Twitter消息通知有一個(gè)讓你回復(fù)或者點(diǎn)贊的部位.
可交互的通知是你通過注冊(cè)通知時(shí)設(shè)置 categories 定義的.每一個(gè)通知分類都可以有多個(gè)預(yù)先自定義的交互.
一旦完成注冊(cè),就可以發(fā)送這個(gè)分類的通知.當(dāng)接收到通知相應(yīng)的交互就可以被用戶獲取.
對(duì)于 WenderCast 應(yīng)用,你將定義一個(gè)自定義"View"動(dòng)作的"News"分類,自定義"View"允許用戶選擇查看,如果用戶選擇就會(huì)在App中直接顯示對(duì)應(yīng)的消息詳細(xì)文章.
添加以下代碼到 registerForPushNotifications(_:): 的開頭.
let viewAction = UIMutableUserNotificationAction()
viewAction.identifier = "VIEW_IDENTIFIER"
viewAction.title = "View"
viewAction.activationMode = .Foreground
這段代碼創(chuàng)建了一個(gè)按鈕標(biāo)題名為"View"的新交互通知,當(dāng)交互通知被用戶觸發(fā)時(shí)打開App并讓其進(jìn)入前臺(tái).這個(gè)交互動(dòng)作的標(biāo)識(shí)符是 VIEW_IDENTIFIER ,這個(gè)標(biāo)識(shí)符被用于區(qū)分同一通知的不同交互動(dòng)作.
添加以下人碼片段至前面代碼之后:
let newsCategory = UIMutableUserNotificationCategory()
newsCategory.identifier = "NEWS_CATEGORY"
newsCategory.setActions([viewAction], forContext: .Default)
這段代碼定義了一個(gè)新通知分類,設(shè)置交互動(dòng)作為之前定義的"View"動(dòng)作,設(shè)置標(biāo)識(shí)符為" NEWS_CATEGORY",這個(gè)標(biāo)識(shí)符你是裝載體要包含的內(nèi)容以用其指示當(dāng)前通知屬于哪個(gè)分類.
最后,通過以下代碼,把新建分類傳遞給UIUserNotificationSettings構(gòu)造方法.
let notificationSettings = UIUserNotificationSettings(forTypes: [.Badge, .Sound, .Alert], categories: [newsCategory])
編繹運(yùn)行,應(yīng)用會(huì)注冊(cè)新通知設(shè)定.按Home鍵來退出當(dāng)前應(yīng)用,以使推送通知能夠顯示.
在你再次運(yùn)行 newspush.php 之前,首先對(duì)指定的分類做一個(gè)改動(dòng).打開 newspush.php 修改通知裝載體使它包含通知的分類標(biāo)識(shí)符:
$body['aps'] = array(
'alert' => $message,
'sound' => 'default',
'link_url' => $url,
'category' => 'NEWS_CATEGORY',
);
保存并關(guān)閉newspush.php,然后運(yùn)行以發(fā)送通知.如果一切進(jìn)展順利,你可以下拉并輕掃顯示的通知你會(huì)看到View按鈕被顯示.
非常好,點(diǎn)擊"View"按鈕將啟動(dòng)WenderCast但不會(huì)做任何事情.為了獲取通知裝載體顯示新的內(nèi)容項(xiàng),你需要在代理方法中做更多的操作.
處理通知交互動(dòng)作事件
回到 AppDelegate.swift,添加另一個(gè)方法:
func application(application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [NSObject : AnyObject], completionHandler: () -> Void) {
// 1
let aps = userInfo["aps"] as! [String: AnyObject]
// 2
if let newsItem = createNewNewsItem(aps) {
(window?.rootViewController as? UITabBarController)?.selectedIndex = 1
// 3
if identifier == "VIEW_IDENTIFIER", let url = NSURL(string: newsItem.link) {
let safari = SFSafariViewController(URL: url)
window?.rootViewController?.presentViewController(safari, animated: true, completion: nil)
}
}
// 4
completionHandler()
}
當(dāng)用戶通過通知的交互動(dòng)作打開應(yīng)用時(shí)這個(gè)方法將會(huì)被調(diào)用.這看來起好像做了很多事,但是實(shí)際上沒有多少新的東西.這段代碼做了以下事情:
- 獲取 aps 字典
- 根據(jù)獲取到的字典創(chuàng)建 NewItem 并跳到新聞頁.
- 檢查以 identifier 為參數(shù)傳進(jìn)來的交互動(dòng)作的標(biāo)識(shí)符.如果View交互動(dòng)作的標(biāo)識(shí)符和鏈接有效則用 SFSafariViewController 顯示這個(gè)鏈接內(nèi)容.
- 在處理完用戶交互動(dòng)用之后調(diào)用系統(tǒng)傳遞給你的 completionHandler 回調(diào).
編繹運(yùn)行,退出App,發(fā)送通知.但請(qǐng)確保下面的URL中有效的:
$ php newspush.php 'New Posts!' 'https://raywenderlich.com'
點(diǎn)擊通知的交互動(dòng)作,在WenderCast應(yīng)用啟動(dòng)后會(huì)立即展示Safari控制器.
恭喜,你剛剛已經(jīng)完成了可交互通知的實(shí)現(xiàn)!嘗試多發(fā)送幾次通知,并用不同的方法打開通知觀察通知的展現(xiàn)行為.
靜默推送通知
靜默推送通知可以靜默方式的喚醒你的app并讓它在后臺(tái)執(zhí)行任務(wù).WenderCast可以利用這個(gè)特性悄悄地刷新播客列表.
正如你所想象的,配合合適的服務(wù)端這個(gè)功能會(huì)非常有用.你不需要不斷的主動(dòng)獲取數(shù)據(jù),當(dāng)有數(shù)據(jù)可獲取時(shí)僅僅只需要發(fā)送一個(gè)靜默通知.
開始之前進(jìn)入 App Settings -> Capabilites 并打開 WenderCast的 Background Modes. 檢查最后一個(gè)選項(xiàng), Remote Notifications 是否勾選.
現(xiàn)在你的app接收到某個(gè)靜默通知就可以在后臺(tái)喚醒.
在AppDelegate內(nèi),用下面更強(qiáng)大的版本替換 application(_:didReceiveRemoteNotification:) 方法:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
let aps = userInfo["aps"] as! [String: AnyObject]
// 1
if (aps["content-available"] as? NSString)?.integerValue == 1 {
// Refresh Podcast
// 2
let podcastStore = PodcastStore.sharedStore
podcastStore.refreshItems { didLoadNewItems in
// 3
completionHandler(didLoadNewItems ? .NewData : .NoData)
}
} else {
// News
// 4
createNewNewsItem(aps)
completionHandler(.NewData)
}
}
這段代碼:
- 檢查 content-available 是否為1,以確定是否是靜默推送.
- 刷新播客列表,因?yàn)樾枰L問網(wǎng)絡(luò)所以刷新列表是異步的.
- 當(dāng)刷新完列表,調(diào)用 completionHandler 回調(diào)方法,讓系統(tǒng)知道數(shù)據(jù)是否已經(jīng)下載.
- 如果不是靜默通知,假定它是消息并創(chuàng)建一個(gè)新的消息項(xiàng).
必需要確保 completionHandler(_:) 方法被調(diào)用并傳遞真實(shí)的是否獲取到數(shù)據(jù)的結(jié)果.系統(tǒng)會(huì)根據(jù)回調(diào)計(jì)算耗電量和app在后臺(tái)的時(shí)間,系統(tǒng)會(huì)根據(jù)需要調(diào)節(jié)App的耗電量以及在后臺(tái)的時(shí)間.
以上就是這段代碼所做的事.現(xiàn)在你可以用 contentpush.php 給你的應(yīng)用發(fā)送一個(gè)靜默通知.請(qǐng)務(wù)必確認(rèn)以下設(shè)置腳本的正確性:
// Put your device token here (without spaces):
$deviceToken = '43e798c31a282d129a34d84472bbdd7632562ff0732b58a85a27c5d9fdf59b69';
// Put your private key's passphrase here:
$passphrase = 'WenderCastPush';
在終端直接運(yùn)行:
$ php contentpush.php
如果一切順利,什么事都不會(huì)發(fā)生.為了看到這段代碼的運(yùn)行結(jié)果,與之前設(shè)置的一樣結(jié)果必須把scheme設(shè)置為"Wait for executable to be launched"并在 application(_:didReceiveRemoteNotification:fetchCompletionHandler:) 方法旁邊打上斷點(diǎn),以確認(rèn)這個(gè)方法會(huì)被調(diào)用.
路在何方?
恭喜你已經(jīng)完成了這份推送通知教程的內(nèi)容并且WenderCast應(yīng)用也有全部的推送功能!
你可以在這里下載完整的工程.記住為了能讓工程正常運(yùn)行你仍然需要更改Bundle ID和證書.
推送通知功能對(duì)于現(xiàn)在的App已經(jīng)是一個(gè)不可或缺的部分,但如果你發(fā)送的通知太頻繁用戶仍然會(huì)調(diào)整你的通知請(qǐng)求許可.對(duì)于一個(gè)深思熟慮的設(shè)計(jì),推送通知會(huì)讓你的應(yīng)用保持足夠的用戶粘性!
這只貓接收到"推送通知"后它就會(huì)知道它的晚餐已經(jīng)準(zhǔn)備好了
我希望你能喜歡這份推送教程.如果你有任何問題,你可以在下面的評(píng)論中隨意提問.