寫這個需求踩了很多坑发笔,記憶深刻了,必須要記錄一下了......
push帶圖片的樣式:
創(chuàng)建 Notification Service Extension
-
選中File->New->Target竿痰,選中NotificationServiceExtension
(坑一 Xcode bug: 我選中File->New->Target,就崩潰旺上,80%概率崩潰叹誉,我也挺崩潰的Xcode版本12.0.1)
-
需要配置NotificationServiceExtension target的Bundle ID,Profile文件(需要在apple開發(fā)者中心配置)忿项。注意team和sign和主target保持一致蓉冈。
-
創(chuàng)建
extension
之后會自動創(chuàng)建一個NotificationService
文件城舞。注意最好不要自己去修改它。(坑二自己作: 我自己最開始創(chuàng)建的時候是OC寞酿,后來被建議換成Swift文件家夺,我就直接把OC文件給刪除了,但是Swift代碼并沒有生效伐弹,應(yīng)該是系統(tǒng)沒有識別出這個文件拉馋,后來又刪掉extension
重新創(chuàng)建的,還是不要瞎折騰的好惨好,折騰的話需要好好研究info.plist
里面的NSExtensionPrincipalClass
煌茴,猜測。這里我直接用暴力刪除重建的方式解決了日川,不過感興趣的可以研究)
代碼蔓腐,解析的時候注意自己url的字典層次結(jié)構(gòu),自行修改龄句,這里的代碼和下面我發(fā)的樣例匹配
import UserNotifications
import CommonCrypto
class NotificationService: UNNotificationServiceExtension {
static let notificationServiceImageAttachmentIdentifier = "com.notificationservice.imagedownloaded"
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
let imagekey = "smallImage"
let dataKey = "data"
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
guard let bestAttemptContent = bestAttemptContent else {
return
}
//download image
let userInfo = request.content.userInfo
guard let data = userInfo[dataKey] as? [String: Any],
let image = data[imagekey] as? String, !image.isEmpty,
let imageURL = URL(string: image) else {
contentHandler(bestAttemptContent)
return
}
//此處回傳一個description回论,是為了方便調(diào)試發(fā)生錯誤的點在哪,通過修改bestAttemptContent.title = description分歇。不過后來我找到了能走到斷點的方式了
downloadAndSave(url: imageURL) { (localURL, description) in
guard let localURL = localURL, let attachment = try? UNNotificationAttachment(identifier: NotificationService.notificationServiceImageAttachmentIdentifier, url: localURL, options: nil) else {
contentHandler(bestAttemptContent)
return
}
bestAttemptContent.attachments = [attachment]
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
private func downloadAndSave(url: URL, handler: @escaping (_ localURL: URL?, _ des: String) -> Void) {
let task = URLSession.shared.dataTask(with: url) { (data, res, error) in
var localURL: URL? = nil
guard let data = data else {
handler(nil, "data is null")
return
}
let ext = (url.absoluteString as NSString).pathExtension
let cacheURL = FileManager.cacheDir()
let url = cacheURL.appendingPathComponent(url.absoluteString.md5).appendingPathExtension(ext)
guard let _ = try? data.write(to: url) else {
handler(nil, "data write error")
return
}
localURL = url
handler(localURL, "success")
}
task.resume()
}
}
extension FileManager {
class func cacheDir() -> URL {
let dirPaths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)
let cacheDir = dirPaths[0] as String
return URL(fileURLWithPath: cacheDir)
}
}
extension String {
var md5: String {
let data = Data(self.utf8)
let hash = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> [UInt8] in
var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
CC_MD5(bytes.baseAddress, CC_LONG(data.count), &hash)
return hash
}
return hash.map { String(format: "%02x", $0) }.joined()
}
}
- 測試的樣例傀蓉,和上面的代碼層次匹配。注意必須設(shè)置
mutable-content : 1
才會走到extension里面來卿樱,當(dāng)然也要注意開啟了允許通知的權(quán)限僚害。
{
"data": {
"smallImage": "https://onevcat.com/assets/images/background-cover.jpg",
},
"aps": {
"badge": 6,
"alert": {
"subtitle": "sub title",
"body": "Hello Moto!",
"title": "Hi i ii i I I"
},
"sound": "default",
"mutable-content": 1
},
"uri": "https://www.baidu.com/"
}
測試帶圖片的push
-
這個Mac工具
NWPusher
還挺好用的(寶藏),可以發(fā)送push繁调,不用別人配合,通知立馬就到靶草。按照鏈接里面去下載這個mac工具 https://github.com/noodlewerk/NWPusher蹄胰,下載下來是這樣的。
注冊
didRegisterForRemoteNotificationsWithDeviceToken
回調(diào)里面拿到push token奕翔。安裝一個dev環(huán)境下的推送證書(test測試環(huán)境)裕寨,然后在這個工具里選擇這個證書。
數(shù)據(jù)都填好后派继,app回到后臺宾袜,點擊push即可看到效果。
調(diào)試 Notification Service Extension
- 直接運行主app驾窟,在extension里面打的斷點是不會走的庆猫。
-
需要選中extension taget,然后點擊運行绅络,在彈出的框中選擇主app月培,點擊run運行起來嘁字。
- 在
NotificationService
打上斷點 - app退到后臺,用
NWPusher
工具發(fā)送一個圖片的payload杉畜。 - 收到通知時會進(jìn)入斷點
- 開始以為不能調(diào)試纪蜒,也不進(jìn)斷點,直接在
contentHandler(bestAttemptContent)
前修改bestAttemptContent.title
此叠,看我修改的push title是否生效了來測試哪一步出現(xiàn)了問題纯续。
注意點??
- 必須開通通知權(quán)限
- 發(fā)送的
payload
必須包含"mutable-content": 1
才能進(jìn)入extesnion - code sign和team要注意和主target保持一致,否則報以下錯灭袁。
Embedded binary is not signed with the same certificate as the parent app. Verify the embedded binary target's code sign settings match the parent app's.
- 下發(fā)的圖片鏈接默認(rèn)只支持
https
杆烁,若要支持http
需要修改extension中的info.plist。
- 下載小圖保存的沙盒地址是這樣的(驗證app extension和主app是隔離開的简卧,不是同一個沙盒哦)兔魂,
file:///var/mobile/Containers/Data/PluginKitPlugin/EEF3E755-E79B-4C7F-A83F-F20642C805C3/Library/Caches/
。write的圖片在push成功后會被系統(tǒng)刪掉举娩,所以不需要管理文件過多的問題析校。 - pushExtension 是否能訪問主target的文件:可以
-
將需要訪問的那個文件,在extension的target上也打上勾勾
- 如果需要在extension中訪問pod铜涉,那么也需要在extension target中pod進(jìn)入智玻,然后在
NotificationService.swfit
文件中import
。
- 發(fā)送多條通知時芙代,
NotificationService
會創(chuàng)建幾個實例吊奢,還是共用一個:會創(chuàng)建多個,驗證過在NotificationService
打印地址纹烹,不同的通知地址不一致页滚。
- extension的target的支持的iOS的系統(tǒng)和主target保持一致,以免出現(xiàn)部分手機收不到小圖push問題
天坑:同事review代碼時想看下我的需求铺呵,結(jié)果他手機沒顯示小圖(他手機iOS14.3裹驰, iPhone X),懷疑我代碼有問題片挂。我把我手機升級和他一樣的系統(tǒng)幻林,測試沒問題,又試了好幾個別的手機都沒問題音念,到處查資料沪饺,搜索了一天無果。 后來隨機提到重啟手機過沒有闷愤,因為不知為啥他手機升級過后系統(tǒng)bug很多整葡,結(jié)果重啟完再push,他收到圖片push啦肝谭。想哭......還是重啟大法好啊......
天坑:又一手機掘宪,莫名其妙didRegisterForRemoteNotificationsWithDeviceToken
和didFailToRegisterForRemoteNotificationsWithError
不調(diào)用杖虾。那么看看這里
重點是:1. 關(guān)機重啟 2.或wifi bug梢莽,插卡 3.或關(guān)機插卡
在你崩潰之前湃交,記得重啟手機豹缀,說不定很多問題壓根不用解決。
不過經(jīng)歷上件事情鼠次,如果沒有重啟更哄,我還是定位不到原因(因為所有的條件都滿足,沒有原因呀)腥寇,這種情況下成翩,要如何解決問題,不被block需求值得思考赦役,歡迎討論和指導(dǎo)麻敌。
參考: