通知相關(guān)系列文章
iOS10 之前通知使用介紹
[iOS] 通知詳解: UIUserNotification
iOS10 相關(guān)API
[iOS] 通知詳解:iOS 10 UserNotifications API
iOS10 本地/遠(yuǎn)程通知
[iOS] 通知詳解: iOS 10 UserNotifications
iOS10 通知附加包
[iOS] 通知詳解: iOS 10 UserNotifications -- 附加包Media Attachments
iOS10 自定義UI
[iOS] 通知詳解: iOS 10 UserNotifications -- 自定義通知UI
申請(qǐng)通知權(quán)限并注冊(cè)通知
同樣,發(fā)送通知需要申請(qǐng)用戶同意,在didFinishLaunchingWithOptions中進(jìn)行權(quán)限的檢測(cè)及請(qǐng)求權(quán)限:
extension AppDelegate: UNUserNotificationCenterDelegate {
func registerAPNs10() {
let center = UNUserNotificationCenter.current()
center.delegate = self
center.getNotificationSettings { (settings) in
if settings.authorizationStatus == .notDetermined {
// 未選擇,請(qǐng)求授權(quán)
center.requestAuthorization(options: [.alert, .sound, .badge]) { (result, error) in
if result {
// 用戶同意
// 注冊(cè)通知,獲取deviceToken
self.registerForRemoteNotifications()
} else {
print(error)
}
}
} else if settings.authorizationStatus == .authorized {
// 已授權(quán)
// 注冊(cè)通知,獲取deviceToken
self.registerForRemoteNotifications()
} else if settings.authorizationStatus == .denied {
// 拒絕,當(dāng)用戶拒絕后,需要在合適的時(shí)機(jī)進(jìn)行提醒,而不應(yīng)該是每次啟動(dòng)時(shí)都去提醒
// 彈出提示框,引導(dǎo)用戶開(kāi)啟
// if let url = URL(string: UIApplication.openSettingsURLString) {
// if UIApplication.shared.canOpenURL(url) {
// UIApplication.shared.open(url, options: [:], completionHandler: nil)
// }
// }
}
}
}
// 注冊(cè)通知,獲取deviceToken
func registerForRemoteNotifications () {
// 請(qǐng)求授權(quán)時(shí)異步進(jìn)行的,這里需要在主線程進(jìn)行通知的注冊(cè)
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
這里需要注意的是,申請(qǐng)權(quán)限的操作是異步進(jìn)行的,我們需要在主線程調(diào)用注冊(cè)遠(yuǎn)程通知的方法UIApplication.shared.registerForRemoteNotifications(),成功或者失敗會(huì)分別調(diào)用下面的代理方法:
// registerForRemoteNotifications 成功的回調(diào),獲取到token
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let deviceStr = deviceToken.map { String(format: "%02hhx", $0) }.joined()
print(deviceStr)
}
// registerForRemoteNotifications 失敗的回調(diào)
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
}
正常獲取到deviceToken,即表示同住注冊(cè)成功,即可進(jìn)行正常的消息推送了。
本地通知
iOS10中本地通知的使用和之前版本使用方式區(qū)別不大,只是使用不同的API來(lái)創(chuàng)建通知,這里只給出一些配置的示例,其他的直接查看相關(guān)的API或者文章的介紹。
一般的本地通知
例如10s觸發(fā)的本地通知,可以這樣設(shè)置:
// 創(chuàng)建通知內(nèi)容
let content = UNMutableNotificationContent()
content.title = "ios 10 local push test"
content.subtitle = "local push subtitle"
content.body = "這是一個(gè)iOS 10 之后的本地通知測(cè)試文本,這里顯示的是消息的詳細(xì)內(nèi)容"
content.sound = .default
content.userInfo = ["info": "這里的信息是傳遞給app的payload內(nèi)容"]
// 創(chuàng)建觸發(fā)方式,10s后觸發(fā)
let timer = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
// 創(chuàng)建通知請(qǐng)求
let req = UNNotificationRequest(identifier: "reqid", content: content, trigger: timer)
// 添加請(qǐng)求到通知中心
UNUserNotificationCenter.current().add(req) { (error) in
print(error)
}
效果:

如果是某個(gè)固定日期時(shí)間或者某個(gè)地理范圍觸發(fā)的通知,可以根據(jù)上面第一部分的UNNotificationTrigger創(chuàng)建相應(yīng)的觸發(fā)器,添加到請(qǐng)求里即可!
帶有交互的本地通知
涉及到的相關(guān)類,參考第一部分的 通知快捷操作Action 部分內(nèi)容。
點(diǎn)擊事件的交互 UNNotificationAction
// 如果點(diǎn)擊后,需要喚起app,則options選擇UNNotificationActionOptions.foreground
let okAction = UNNotificationAction(identifier: "okidentifier", title: "查看", options: UNNotificationActionOptions.foreground)
// 如果點(diǎn)擊后,不需要喚起app,則options選擇另外兩個(gè)
let cancel = UNNotificationAction(identifier: "cancelidentifier", title: "關(guān)閉", options: UNNotificationActionOptions.destructive)
let category = UNNotificationCategory(identifier: "categoryidentifier", actions: [okAction, cancel], intentIdentifiers: [], options: UNNotificationCategoryOptions.customDismissAction)
UNUserNotificationCenter.current().setNotificationCategories(Set.init([category]))
// 創(chuàng)建通知內(nèi)容
let content = UNMutableNotificationContent()
content.title = "ios 10 local push test"
content.subtitle = "local push subtitle"
content.body = "這是一個(gè)iOS 10 之后的本地通知測(cè)試文本,這里顯示的是消息的詳細(xì)內(nèi)容"
content.sound = .default
content.userInfo = ["info": "這里的信息是傳遞給app的payload內(nèi)容"]
content.categoryIdentifier = "categoryidentifier"
// 創(chuàng)建觸發(fā)方式,10s后觸發(fā)
let timer = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
// 創(chuàng)建通知請(qǐng)求
let req = UNNotificationRequest(identifier: "reqid", content: content, trigger: timer)
// 添加請(qǐng)求到通知中心
UNUserNotificationCenter.current().add(req) { (error) in
print(error)
}

點(diǎn)擊按鈕后會(huì)調(diào)用代理方法
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
print(response.actionIdentifier)
}
快捷回復(fù)輸入框的action UNTextInputNotificationAction
只需要?jiǎng)?chuàng)建UNTextInputNotificationAction的實(shí)例,添加到category中即可:
let okAction = UNTextInputNotificationAction(identifier: "okidentifier", title: "回復(fù)", options: .foreground, textInputButtonTitle: "快捷回復(fù)", textInputPlaceholder: "請(qǐng)輸入。。。")
let cancel = UNNotificationAction(identifier: "cancelidentifier", title: "關(guān)閉", options: UNNotificationActionOptions.destructive)
let category = UNNotificationCategory(identifier: "categoryidentifier", actions: [okAction, cancel], intentIdentifiers: [], options: UNNotificationCategoryOptions.customDismissAction)
UNUserNotificationCenter.current().setNotificationCategories(Set.init([category]))
// 創(chuàng)建通知內(nèi)容
let content = UNMutableNotificationContent()
content.title = "ios 10 local push test"
content.subtitle = "local push subtitle"
content.body = "這是一個(gè)iOS 10 之后的本地通知測(cè)試文本,這里顯示的是消息的詳細(xì)內(nèi)容"
content.sound = .default
content.userInfo = ["info": "這里的信息是傳遞給app的payload內(nèi)容"]
content.categoryIdentifier = "categoryidentifier"
// 創(chuàng)建觸發(fā)方式,10s后觸發(fā)
let timer = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
// 創(chuàng)建通知請(qǐng)求
let req = UNNotificationRequest(identifier: "reqid", content: content, trigger: timer)
// 添加請(qǐng)求到通知中心
UNUserNotificationCenter.current().add(req) { (error) in
print(error)
}
點(diǎn)擊之后的效果:

同樣,在代理方法中可獲取輸入的內(nèi)容:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
print(response.actionIdentifier)
if let input = response as? UNTextInputNotificationResponse {
print(input.userText)
}
completionHandler()
}
移除通知
// 移除某個(gè)已觸發(fā)的通知
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: ["reqid"])
// 移除添加的通知請(qǐng)求 UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["reqid"])
// 移除所有已執(zhí)行的通知
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
// 移除所有通知請(qǐng)求 UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
遠(yuǎn)程通知 APNs(Apple Push Notification Services)
遠(yuǎn)程通知在獲取到通知權(quán)限后,就可以使用一般的 Payload 來(lái)發(fā)送通知。
一般消息推送的Payload模板
{
"aps":
{
"alert":
{
"title":"iOS10 標(biāo)題",
"subtitle" : "iOS10 副標(biāo)題",
"body":"這是iOS10中遠(yuǎn)程推送的消息主體內(nèi)容,這里的內(nèi)容會(huì)展示在消息摘要信息里,當(dāng)下拉消息框,就能夠看到完整的消息內(nèi)容。"
},
"badge":1,
"sound":"default",
"customInfo":{"key":"這是自定義的消息內(nèi)容"}
}
}
效果圖:

當(dāng)app在前臺(tái)運(yùn)行時(shí),同樣會(huì)走willPresent notification代理方法;當(dāng)app后臺(tái)運(yùn)行或未打開(kāi)時(shí),走didReceive response方法;沒(méi)錯(cuò),和本地通知走的代理方法是一樣的。我們可以通過(guò)trigger的類型來(lái)判斷是本地通知,還是遠(yuǎn)程通知
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
print(response.actionIdentifier)
if let input = response as? UNTextInputNotificationResponse {
print(input.userText)
}
if response.notification.request.trigger is UNPushNotificationTrigger {
// 遠(yuǎn)程通知
} else {
// 本地通知
}
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
print(notification.request.content.categoryIdentifier)
let userInfo = notification.request.content.userInfo
print(userInfo)
if notification.request.trigger is UNPushNotificationTrigger {
// 前臺(tái)運(yùn)行時(shí)遠(yuǎn)程通知
} else {
// 前臺(tái)運(yùn)行時(shí)本地通知
}
completionHandler(.alert)
}
帶有交互action的遠(yuǎn)程通知
只需要在請(qǐng)求中心添加相應(yīng)的交互category,并把其identifier添加到Payload中即可;
定義所需的交互 UNNotificationAction
let okAction = UNNotificationAction(identifier: "okidentifier", title: "查看", options: UNNotificationActionOptions.foreground)
// let okAction = UNTextInputNotificationAction(identifier: "okidentifier", title: "回復(fù)", options: .foreground, textInputButtonTitle: "快捷回復(fù)", textInputPlaceholder: "請(qǐng)輸入。。。")
let cancel = UNNotificationAction(identifier: "cancelidentifier", title: "關(guān)閉", options: UNNotificationActionOptions.destructive)
let category = UNNotificationCategory(identifier: "categoryidentifier", actions: [okAction, cancel], intentIdentifiers: [], options: UNNotificationCategoryOptions.customDismissAction)
UNUserNotificationCenter.current().setNotificationCategories(Set.init([category]))
此時(shí)的Payload,需要加入 category,告訴系統(tǒng)需要加載哪一組的交互
{
"aps":
{
"alert":
{
"title":"iOS10 標(biāo)題",
"subtitle" : "iOS10 副標(biāo)題",
"body":"這是iOS10中遠(yuǎn)程推送的消息主體內(nèi)容,這里的內(nèi)容會(huì)展示在消息摘要信息里,當(dāng)下拉消息框,就能夠看到完整的消息內(nèi)容。這是一個(gè)帶有交互按鈕的通知。。。"
},
"category":"categoryidentifier",
"badge":1,
"sound":"default"
}
}
帶有交互按鈕的遠(yuǎn)程push

如果是需要快捷回復(fù)框,只需要像本地通知一樣,創(chuàng)建一個(gè)UNTextInputNotificationAction 實(shí)例即可!
靜默通知
發(fā)送靜默通知,同iOS10之前的方式一致,修改Payload如下即可:
{"aps":{"content-available":"1","extinfo": {"info": "Test remote info"}}}
需要注意的是,靜默push不會(huì)走iOS10中新的代理方法,還是在原來(lái)的代理方法進(jìn)行接收反饋:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
let state = UIApplication.shared.applicationState
var msg = ""
if state == .active {
// qiantai
msg += "active"
} else {
// houtai
msg += "inactive"
}
print("Receive remote notification at \(msg)")
print(userInfo)
let info = ["dele":"zhe shi ling yi ge push!",
"info": userInfo] as [String : Any]
TestSingle.shared.info = info
completionHandler(.noData)
}