前言
手機(jī)推送功能是比較常見的需求,由于墻的原因瀏覽器推送宛如雞肋,最近公司業(yè)務(wù)需要面向海外市場,所以計劃給網(wǎng)頁添加推送功能。
手機(jī) app 推送國內(nèi)有很多的推送廠商可以選擇(ex:JPush),但是瀏覽器推送由于大環(huán)境影響基本沒有類似產(chǎn)品,只能手?jǐn)](聽說 JPush 正在著手瀏覽器推送,很是期待)。聽說我司的產(chǎn)品(iOS、Android app)使用極光推送已經(jīng) 3 年了,為什么是聽說,因為我才入職兩年,手動 [捂臉]。
本文將主要介紹兩種網(wǎng)頁推送,要求網(wǎng)頁支持 HTTPS。
支持 Push Api 的瀏覽器
現(xiàn)在主流的現(xiàn)代瀏覽器都支持 Web Push API ,web push 允許瀏覽器打開的時候接收到推送(就算頁面被關(guān)閉也能接受)。
Push APi 要求網(wǎng)頁支持 HTTPS,因為 Push API 使用 Service Work 只能在 HTTPS 網(wǎng)站上使用,但是如果測試的話可以直接使用 localhost 也是允許的。
Push APi 推送的全流程如下圖所示:

瀏覽器通過如下代碼完成:
// https://example.com/webapp.js
navigator.serviceWorker.register('serviceworker.js').then(
function(serviceWorkerRegistration) {
serviceWorkerRegistration.pushManager.subscribe().then(
function(pushSubscription) {
console.log(pushSubscription.endpoint);
console.log(pushSubscription.getKey('p256dh'));
console.log(pushSubscription.getKey('auth'));
// The push subscription details needed by the application
// server are now available, and can be sent to it using,
// for example, an XMLHttpRequest.
}, function(error) {
// During development it often helps to log errors to the
// console. In a production environment it might make sense to
// also report information about errors back to the
// application server.
console.log(error);
}
);
});
我們需要注冊一個 ServiceWork,成功注冊后會得到一個 serviceWorkerRegistration,可以使用 serviceWorkerRegistration.pushManager.subscribe 這個方法來訂閱推送成功訂閱后返回 pushSubscription 用于標(biāo)識當(dāng)前當(dāng)前的網(wǎng)頁用戶,需要傳到自己的應(yīng)用服務(wù)中用于推送。注意現(xiàn)在是能收到推送,但是不發(fā)觸發(fā)通知,我們可以在 servicework.js 文件中添加 通知邏輯:
self.addEventListener('push', function(event) {
console.log('[Service Worker] Push Received.');
console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);
const title = 'Push Codelab';
const options = {
body: 'Yay it works.',
icon: 'images/icon.png',
badge: 'images/badge.png'
};
event.waitUntil(self.registration.showNotification(title, options));
});
服務(wù)端等到 pushSubscription 后可以通過里面的參數(shù) auth 和 p256dh 給 endpoint 發(fā)送推送請求。
到此支持 Push Api 瀏覽推送就到此結(jié)束。
Safari 的 APNs 推送
Safari 的沒有支持 Push Api 功能,但 OS X v10.9 之后可以使用 APNs 服務(wù)來支持推送功能,且功能更強(qiáng)大,允許 Safari 在關(guān)閉的情況下接受推送通知。

safari apns 要求網(wǎng)頁支持 HTTPS,不然無法請求推送,本地測試的話可以使用自建證書來測試。
Safari 推送和手機(jī)應(yīng)用推送都走的 APNS 通道,由于公司的手機(jī) APP 已經(jīng)支持了推送,后臺使用 token 做驗證,所以后臺基本不需要修改推送邏輯,非常的簡單。
注冊 Website Push IDs
首先需要在 Certificates, Identifiers & Profiles
中注冊 Website Push IDs

構(gòu)建 Push Package
當(dāng) Safari 請求推送權(quán)限的時候,Safari 會向自己的服務(wù)器請求 Push Package,如果請求失敗會返回錯誤,所以我們需要先創(chuàng)建這個 Push Package。
Push Package 最終要包含如下文件
BayAirlines.pushpackage/
icon.iconset/
icon_16x16.png
icon_16x16@2x.png
icon_32x32.png
icon_32x32@2x.png
icon_128x128.png
icon_128x128@2x.png
manifest.json
signature
website.json
icon.iconset/ 里面包含了推送所需的 icon。
website.json 主要包含網(wǎng)頁信息。
Manifest.json 會記錄所有的文件的 hash 值。大概長這樣
{
"website.json": {
"hashType": "sha512",
"hashValue": "4309a0cf6ee37909423fb4f5f762eb530d4a7cfe9e1d30b9c85b602afb9296a508f5df00c6f63439e4dcc4484aae4cd77d1f632678ece0ef1b62ca6c9cbdebb3"
},
"icon.iconset/icon_16x16.png": {
"hashType": "sha512",
"hashValue": "8bb462e25ca79cca241355f5ae97ffab352acf7d2a0390f6547f1d0a55ade59e01e725bea70778ad2822b1ec137a8c0547197727ae2e9fed620b0d3ddea6803b"
},
...
}
幸運的是 Apple 提供了一個工具讓我們很方便的生成 website.json 和 signature 這兩個文件, 我們只需要準(zhǔn)備好 icon.iconset/ 和 website.json 即可。createPushPackage.php 我們會得到 PushPackage 文件。
此時我們還需要給自己的應(yīng)用服務(wù)添加一個 API,返回剛剛生成的 PushPackage 文件。
webServiceURL/version/pushPackages/websitePushID
請求權(quán)限
請求推送權(quán)限的流程可以整理成下圖所示:

- 調(diào)用
window.safari.pushNotification.permission("< 你的 應(yīng)用 id>")可以獲得當(dāng)前的應(yīng)用權(quán)限,如果當(dāng)前權(quán)限為 default 則表示之前沒有請求過。可以調(diào)用
window.safari.pushNotification.requestPermission(
'The web service URL.', //
'The Website Push ID.', //
{}, // Data that you choose to send to your server to help you identify the user.
function () { } // 請求結(jié)果回調(diào)
);
注意調(diào)用 requestPermission 時,safari 回去獲取 push package 這個文件,如果之前的不步驟沒有完成這里會失敗。如果成功則會彈框提醒用戶是否要運行當(dāng)前網(wǎng)站接收推送通知。
用戶同意后可以通過 window.safari.pushNotification.permission("< 你的 應(yīng)用 id>") 方法獲得 permission object,permission. deviceToken 需要傳到自己服務(wù)器用于給 APNS 發(fā)送推送請求。到此 safari 推送已經(jīng)完成。
Safari 推送和 iOS app 推送都是走的 APNS 服務(wù),由于之前應(yīng)用(react native 應(yīng)用)已經(jīng)集成了推送功能使用 jpush-react-native 這個插件,而且使用 token 的驗證方式,所有服務(wù)端那邊需要修改的東西比較少,只需要修改web 應(yīng)用的 keyId 即可。
done!
PS:為了極光征文大賽才注冊的簡書,平常文章都是放 GitHub 的,希望下次活動可以支持 Github 上的文章。
「本文為極光征文參賽文章」