Swift - UserNotifications框架使用詳解6(ServiceExtension、多媒體內(nèi)容推送)

十二、使用 Notification Service Extension 攔截并修改通知

iOS 10?中添加了兩個與通知相關的?extension:Service Extension?和?Content Extension。本文先介紹下前者。

1,基本介紹

Service Extension?目前只對遠程推送的通知有效。

Service Extension?可以讓我們有機會在收到遠程推送通知后,展示之前對通知內(nèi)容進行修改。

通過本機截取推送并替換內(nèi)容的方式,我們可以實現(xiàn)端到端 (end-to-end) 的推送加密:

我們在服務器推送?payload?中加入加密過的文本,在客戶端接到通知后使用預先定義或者獲取過的密鑰進行解密,然后立即顯示。

這樣一來,即使推送信道被第三方截取,其中所傳遞的內(nèi)容也還是安全的。使用這種方式來發(fā)送密碼或者敏感信息,對于一些金融業(yè)務應用和聊天應用來說,應該是必備的特性。

2,使用說明

(1)首先我們點擊"File" -> "New" -> "Target...",使用?NotificationService?的模板來創(chuàng)建一個?NotificationService。

(2)NotificationService?的模板已經(jīng)自動為我們生成了一些基本代碼,這里對其稍作修改(自動給通知內(nèi)容后面加上一個小尾巴)。NotificationService?里特別要注意如下兩個方法:

1,didReceive

該方法中有一個等待發(fā)送的通知請求。我們通過修改這個請求中的?content?內(nèi)容,然后在限制的時間內(nèi)將修改后的內(nèi)容通過調(diào)用?contentHandler?返還給系統(tǒng),就可以顯示這個修改過的通知了。

2,serviceExtensionTimeWillExpire

在一定時間內(nèi)如果沒有調(diào)用?contentHandler?的話,系統(tǒng)會調(diào)用這個方法,來告訴我們時間到了:

我們可以什么都不做,這樣的話系統(tǒng)便當作什么都沒發(fā)生,簡單地顯示原來的通知。

或許我們已經(jīng)設置好了絕大部分內(nèi)容,只是有很少一部分沒有完成。這時我們也可以像例子中這樣調(diào)用?contentHandler?來顯示一個變更到一半的通知。

import?UserNotifications


class?NotificationService:?UNNotificationServiceExtension?{


????var?contentHandler: ((UNNotificationContent) ->?Void)?

????var?bestAttemptContent:?UNMutableNotificationContent?


????//我們可以在后臺處理接收到的推送,讓后傳遞修改后的的內(nèi)容給contentHandler進行展示

????override?func?didReceive(_ request:?UNNotificationRequest,

????????withContentHandler contentHandler:?@escaping?(UNNotificationContent) ->?Void) {

????????self.contentHandler = contentHandler

????????bestAttemptContent = (request.content.mutableCopy()?as??UNMutableNotificationContent)


????????if?let?bestAttemptContent = bestAttemptContent {

????????????//給通知內(nèi)容添加個小尾巴

????????????bestAttemptContent.body =?"\(bestAttemptContent.body) 【來自hangge.com】"


????????????contentHandler(bestAttemptContent)

????????}

????}


????//如果我們獲取消息后一段時間內(nèi)沒有調(diào)用 contentHandler 的話,系統(tǒng)會調(diào)用這個方法

????override?func?serviceExtensionTimeWillExpire() {

????????//如果消息沒處理好,我們也將這個沒處理完畢的消息進行展示

????????if?let?contentHandler = contentHandler,?let?bestAttemptContent =? bestAttemptContent {

????????????contentHandler(bestAttemptContent)

????????}

????}

}

(3)如果需要調(diào)試這個通知擴展類,注意?Target?要選擇?NotificationService,然后編譯運行時選擇我們的程序。

(4)同時?Service Extension?的發(fā)布版本要低于設備的版本(比如我手機是?10.3.1,那么這里可以直接設置為?10)。

(5)最后我們在遠程通知的?payload?中增加一個?mutable-content?值為?1?的項來啟用內(nèi)容修改(這個一定要有,否則可能會攔截通知失?。?/p>

{

????"aps": {

????????"alert": {

????????????"title":?"最新資訊",

????????????"body":?"2017全國文明城市公布"

????????},

????????"sound":?"default",

????????"badge": 1,

????????"mutable-content": 1

????},

}

(6)可以看到客戶端這邊收到通知后,會自動在內(nèi)容尾部增加一個段小尾巴(【來自hangge.com】)

十三、為本地通知添加多媒體內(nèi)容

多媒體推送是?iOS10?新增加的一個功能。我們可以在通知中嵌入圖片或者視頻,這極大地豐富了推送內(nèi)容的可讀性和趣味性。


1,使用說明

為本地通知添加多媒體內(nèi)容十分簡單,只需要通過本地磁盤上的文件?URL?創(chuàng)建一個?UNNotificationAttachment?對象,然后將這個對象放到數(shù)組中賦值給?content?的?attachments?屬性就可以了。

attachments?雖然是一個數(shù)組,但是系統(tǒng)只會展示第一個?attachment?對象的內(nèi)容。不過我們依然可以發(fā)送多個?attachments,然后在要展示的時候再重新安排它們的順序,以顯示最符合情景的圖片或者視頻。另外,我們也可能會在自定義通知展示?UI?時用到多個?attachment,這個我們下文會進行演示。

系統(tǒng)在創(chuàng)建?attachement?時會根據(jù)提供的?url?后綴確定文件類型,如果沒有后綴,或者后綴不正確的話,我們可以在創(chuàng)建時通過?UNNotificationAttachmentOptionsTypeHintKey?來指定資源類型。

2,多媒體文件的格式、尺寸限制

(1)支持的最大尺寸:

圖片:10MB

音頻:5MB

視頻:50MB

(2)支持的文件格式:

圖片:kUTTypeJPEG、kUTTypeGIF、kUTTypePNG

音頻:kUTTypeAudioInterchangeFileFormat、kUTTypeWaveformAudio、kUTTypeMP3、kUTTypeMPEG4Audio

視頻:kUTTypeMPEG、kUTTypeMPEG2Video、kUTTypeMPEG4、kUTTypeAVIMovie

3,使用樣例

(1)下面代碼我們給通知附帶上一張圖片:

import?UIKit

import?UserNotifications


class?ViewController:?UIViewController?{


????override?func?viewDidLoad() {

????????super.viewDidLoad()


????????//設置推送內(nèi)容

????????let?content =?UNMutableNotificationContent()

????????content.title =?"hangge.com"

????????content.body =?"囤積iPhoneX的黃牛賠到懷疑人生?"


????????//給通知添加圖片附件

????????if?let?imageURL =?Bundle.main.url(forResource:?"image", withExtension:?"png"),

????????????let?attachment = try??UNNotificationAttachment(identifier:?"imageAttachment",

???????????????????????????????????????????????????????????url: imageURL, options:?nil) {

????????????content.attachments = [attachment]

????????}


????????//設置通知觸發(fā)器

????????let?trigger =?UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats:?false)


????????//設置請求標識符

????????let?requestIdentifier =?"com.hangge.testNotification"


????????//設置一個通知請求

????????let?request =?UNNotificationRequest(identifier: requestIdentifier,

????????????????????????????????????????????content: content, trigger: trigger)


????????//將通知請求添加到發(fā)送中心

????????UNUserNotificationCenter.current().add(request) { error?in

????????????if?error ==?nil?{

????????????????print("Time Interval Notification scheduled: \(requestIdentifier)")

????????????}

????????}

????}


????override?func?didReceiveMemoryWarning() {

????????super.didReceiveMemoryWarning()

????}

}

(2)效果圖

上面代碼運行后,在通知顯示時,橫幅或者彈窗將附帶有設置的圖片。

使用?3D Touch pop?通知或者下拉通知顯示詳細內(nèi)容時,圖片也會被放大展示。

除了圖片以外,通知還支持音頻以及視頻。我們可以將?MP3?或者?MP4?這樣的文件提供給系統(tǒng),從而在通知中進行展示和播放。

?

4,訪問已創(chuàng)建的 attachment 的內(nèi)容

我們可以訪問一個已經(jīng)創(chuàng)建的?attachment?的內(nèi)容,但是要注意權(quán)限問題??梢允褂?startAccessingSecurityScopedResource?來暫時獲取已創(chuàng)建的?attachment?的訪問權(quán)限。

let?content = notification.request.content

if?let?attachment = content.attachments.first {

????if?attachment.url.startAccessingSecurityScopedResource() {

????????eventImage.image =?UIImage(contentsOfFile: attachment.url.path!)

????????attachment.url.stopAccessingSecurityScopedResource()

????}

}

十四、為遠程推送添加多媒體內(nèi)容

1,實現(xiàn)原理

對于遠程推送,我們也可以顯示圖片等多媒體內(nèi)容。不過需要通過上面介紹的?Notification Service Extension?來修改推送通知內(nèi)容的技術(shù)。具體流程如下:

我們在推送的?payload?中指定需要加載的圖片資源地址,這個地址可以是應用?bundle?內(nèi)已經(jīng)存在的資源,也可以是網(wǎng)絡的資源。

客戶端收到通知后,根據(jù)資源地址創(chuàng)建相應的?UNNotificationAttachment。由于只能使用本地資源創(chuàng)建?UNNotificationAttachment,所以如果多媒體還不在本地的話,我們需要先將其下載到本地。

在完成?UNNotificationAttachment?創(chuàng)建后,我們就可以像本地通知一樣,將它設置給?attachments?屬性,然后調(diào)用?contentHandler?了。

2,使用樣例

(1)假設我們的遠程通知?payload?報文如下,其中:

mutable-content:表示我們會在接收到通知時需要對內(nèi)容進行更改。

image:表示需要顯示的圖片的地址。

{

????"aps": {

????????"alert": {

????????????"title":?"最新資訊",

????????????"body":?"2017全國文明城市公布"

????????},

????????"sound":?"default",

????????"badge": 1,

????????"mutable-content": 1

????},

????"image":?"https://img1.gtimg.com/ninja/2/2017/05/ninja149447456097353.jpg"

}

(2)項目這邊創(chuàng)建一個?NotificationService,作用是接收到上面這樣的通知時會自動提取圖片地址、下載,并生成?attachment,然后進行通知展示。

import?UserNotifications


class?NotificationService:?UNNotificationServiceExtension?{


????var?contentHandler: ((UNNotificationContent) ->?Void)?

????var?bestAttemptContent:?UNMutableNotificationContent?


????//我們可以在后臺處理接收到的推送,讓后傳遞修改后的的內(nèi)容給contentHandler進行展示

????override?func?didReceive(_ request:?UNNotificationRequest,

?????????????????????????????withContentHandler contentHandler:

????????@escaping?(UNNotificationContent) ->?Void) {

????????self.contentHandler = contentHandler

????????bestAttemptContent = (request.content.mutableCopy()?as??UNMutableNotificationContent)


????????if?let?bestAttemptContent = bestAttemptContent {

????????????//將遠程推送通知中的圖片下載到本地,并顯示

????????????if?let?imageURLString = bestAttemptContent.userInfo["image"]?as??String,

????????????????let?URL?=?URL(string: imageURLString) {

????????????????downloadAndSave(url:?URL) { localURL?in

????????????????????if?let?localURL = localURL {

????????????????????????do {

????????????????????????????let?attachment = try?UNNotificationAttachment(identifier:?"download",

??????????????????????????????????????????????????????????????????????????url: localURL,

??????????????????????????????????????????????????????????????????????????options:?nil)

????????????????????????????bestAttemptContent.attachments = [attachment]

????????????????????????} catch {

????????????????????????????print(error)

????????????????????????}

????????????????????}

????????????????????contentHandler(bestAttemptContent)

????????????????}

????????????}

????????}

????}


????//如果我們獲取消息后一段時間內(nèi)沒有調(diào)用 contentHandler 的話,系統(tǒng)會調(diào)用這個方法

????override?func?serviceExtensionTimeWillExpire() {

????????//如果消息沒處理好,我們也將這個沒處理完畢的消息進行展示

????????if?let?contentHandler = contentHandler,?let?bestAttemptContent =? bestAttemptContent {

????????????contentHandler(bestAttemptContent)

????????}

????}


????//將圖片下載到本地臨時文件夾中

????private?func?downloadAndSave(url:?URL, handler:?@escaping?(_ localURL:?URL?) ->?Void) {

????????let?task =?URLSession.shared.dataTask(with: url, completionHandler: {

????????????data, res, error?in

????????????var?localURL:?URL? =?nil

????????????if?let?data = data {

????????????????//取得當前時間的時間戳

????????????????let?timeInterval =?Date().timeIntervalSince1970

????????????????let?timeStamp =?Int(timeInterval)

????????????????//文件后綴

????????????????let?ext = (url.absoluteString?as?NSString).pathExtension

????????????????let?temporaryURL =?FileManager.default.temporaryDirectory

????????????????let?url = temporaryURL.appendingPathComponent("\(timeStamp)")

????????????????????.appendingPathExtension(ext)


????????????????if?let?_ = try? data.write(to: url) {

????????????????????localURL = url

????????????????}

????????????}

????????????handler(localURL)

????????})

????????task.resume()

????}

}

(3)具體效果如下??梢钥吹郊词故沁h程通知,附帶的也是網(wǎng)絡圖片,但也是可以正常顯示的。

原文出自:www.hangge.com??轉(zhuǎn)載請保留原文鏈接:https://www.hangge.com/blog/cache/detail_1852.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容