一、各版本通知比较
- iOS 8以后,APNs推送的字节是2k,iOS8以前是256字节,iOS10现在是4k
- iOS 9以后APNs支持HTTP/2协议栈,优化长连接,具有标准的HTTP返回和管道复用技术
- iOS 10以后,APNs可根据推送消息的唯一标示符查询某条消息是否被用户阅读,可更新某一推送消息,而不用发重读的多条消息,可推送多媒体消息
静默推送(iOS7之后):
应用收到通知后在后台(background)状态下运行一段代码,可用于从服务器获取内容更新;
静默推送:收到推送(没有文字没有声音),不用点开通知,不用打开APP,就能执行,用户完全感觉不到
- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo fetchCompletionHandler:(void(^)(UIBackgroundFetchResultresult))handler
推送格式:
- 一定不能加alert,如果加入了alert就不是静默推送了
- 一定要加"content-available" : 1
- sound也不能加,加入的话也是静默推送,可以测试时添加.
二、iOS10的通知
- iOS 10通知系统支持Images, GIFs, Audio and Video类型
- iOS 10推出Notification Service Extension与Notification Content Extension,可以实现推送数据在展示前进行下载更新、定制通知UI
- iOS 10统一了通知类型,具有时间间隔通知、地理位置通知和日历通知,App在前台获取通知。
1、UNUserNotificationCenter
通知中心,用以管理通知的注册、权限获取和管理、通知的删除与更新,通过代理分发事件等
获取未触发的通知
func getPendingNotificationRequests(completionHandler: @escaping ([UNNotificationRequest]) -> Swift.Void)
获取通知中心列表的通知
func getDeliveredNotifications(completionHandler: @escaping ([UNNotification]) -> Swift.Void)
清除某一个未触发/通知中心的通知
open func removePendingNotificationRequests(withIdentifiers identifiers: [String])
open func removeDeliveredNotifications(withIdentifiers identifiers: [String])
删除所有通知
func removeAllPendingNotificationRequests()
func removeAllDeliveredNotifications()
2、UNNotification
通知实体,在UNUserNotificationCenter的代理回调事件中,告知App接收到一条通知,包含一个发起通知的请求UNNotificationRequest
3、UNNotificationRequest
包含通知内容UNNotificationContent和触发器UNNotificationTrigger
4、UNNotificationContent
通知内容,通知的title,sound,badge以及相关的图像、声音、视频附件UNNotificationAttachment,触发打开App时候指定的LacnchImage等
5、UNNotificationTrigger
iOS10之后,通过2个代理方法来处理通知的接收和点击事件,此外,本地通知跟远程通知合二为一,区分本地通知跟远程通知的类是UNNotificationTrigger.h
。iOS10推送
-
UNPushNotificationTrigger
是APNs通知 -
UNTimeIntervalNotificationTrigger
(本地通知) 一定时间之后发送,可以设置重复(必须大于60s) -
UNCalendarNotificationTrigger
(本地通知) 一定日期之后,可以设置重复,也就是下一相同间隔内也推送 -
UNLocationNotificationTrigger
(本地通知)地理位置的一种通知,当用户进入或离开一个地理区域来通知。在CLRegion标识符必须是唯一的。
6、UNNotificationResponse
用户在触发了按钮或者文本提交的UNNotificationAction的时候,会形成一个response,通过通知中心的代理方法回调给App进行处理或者是交给扩展处理。
7、UNNotificationAction
是通知中添加的action,展示在通知栏的下方。默认以的button样式展示。有一个文本输入的子类UNTextInputNotificationAction。可以在点击button之后弹出一个键盘,输入信息。用户点击信息和输入的信息可以在UNNotificationResponse中获取
UNNotificationActionOptions:foreground(打开APP),destructive(标识红色),authenticationRequired(是否需要解锁)
ios10添加Action
推送的时候必须添加category字段,当和本地设置的字段identifier 相同时才能展示
//创建Action,放在注册的时候调用
@available(iOS 10.0, *)
func ios10ActionRegister(){
let enterAction = UNNotificationAction(identifier: "enterApp", title: "打开应用", options: .foreground)
let inputAction = UNTextInputNotificationAction(identifier: "inputApp", title: "发送评论", options: .authenticationRequired, textInputButtonTitle: "回复", textInputPlaceholder: "请输入文字")
let ingnoreAction = UNNotificationAction(identifier: "ingnoreApp", title: "忽略", options: .destructive)
let category = UNNotificationCategory(identifier: "categoryIdentifier", actions: [enterAction,inputAction,ingnoreAction], intentIdentifiers: [], options: UNNotificationCategoryOptions(rawValue: 0))
UNUserNotificationCenter.current().setNotificationCategories([category])
}
//代理方法中获取输入值
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
ios10HandelActionButton(response)
}
func ios10HandelActionButton(response: UNNotificationResponse){
print("👉👉response.actionIdentifier",response.actionIdentifier)
switch response.actionIdentifier{
case "inputApp":
let inputResponse = response as! UNTextInputNotificationResponse
print("👉👉👉👉\(inputResponse.userText)")
default:
print("")
}
}
iOS8 - ios9 添加Action
//创建Action,放在注册的时候调用
func ios8To9ActionRegister() -> UIMutableUserNotificationCategory{
//添加操作按钮
let action1 = UIMutableUserNotificationAction()
action1.identifier = "enterApp"
action1.activationMode = .foreground
action1.title="打开应用"
action1.isDestructive = false
let action2 = UIMutableUserNotificationAction()
action2.identifier = "inputApp"
action2.title="发送评论"
action2.activationMode = .background
action2.isAuthenticationRequired = false
action2.isDestructive = true
if #available(iOS 9.0, *) {
action2.behavior = .textInput //9.0支持
} else {
}//点击按钮文字输入,是否弹出键盘
let category = UIMutableUserNotificationCategory()
category.identifier = "categoryIdentifier"
category.setActions([action1,action2], for: .default)
return category
}
//在下面的代理方法中处理
func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, completionHandler: @escaping () -> Void) {
//ios8to9 处理本地通知Action
}
func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
//ios8to9 处理远程通知Action
}
8、UNNotificationCategory
该分类包含了某一个通知包含的交互动作的组合,当request里面content属性categoryIdentity和UNNotificationCategory的Identity相同的话,那该通知就会以预定义好的交互按钮或者文本框添加到通知实体上。
9、UNNotificationAttachment
UNNotificationAttachment(附件通知)是指可以包含音频,图像或视频内容,并且可以将其内容显示出来的通知。使用本地通知时,可以在通知创建时,将附件加入即可。
10、UNNotificationServiceExtension
iOS10_UNNotificationServiceExtension 作者 徐不同
ServiceExtension作用:
- 我们可以把即将给用户展示的通知内容,做各种自定义的处理,最终,给用户呈现一个更为丰富的通知。
- 安全性(收到通知后的最多30s内,你可以把你的通知内容,解密后,在重新展示在用户的通知拦上)
11、UNNotificationContentExtension
iOS10_UNNotificationContentExtension 作者 徐不同)
收到远程或者本地通知的时候,弹出一个自定义界面
UNNotificationContentExtension的Info.Plist
info.plistUNNotificationExtensionCategory(必须要有)
就是在收到通知的时候,我们可以让服务器把这个通知的categoryIdentifier带上,可以通过设置UNMutableNotificationContent
的categoryIdentifier
相同,关联当前UNNotificationContentExtension
UNNotificationExtensionInitialContentSizeRatio(必须要有)
这个值的类型是一个浮点类型,代表的是高度与宽度的比值。系统会使用这个比值,作为初始化view的大小。举个简单的例子来说,如果该值为1,则该视图为正方形。如果为0.5,则代表高度是宽度的一半。这个值只是初始化的一个值,在这个扩展添加后,可以重写frame,展示的时候,在我们还没打开这个视图预览时,背景是个类似图片占位的灰色,那个灰色的高度宽度之比,就是通过这个值来设定。
UNNotificationExtensionDefaultContentHidden(可选)
这个值是一个BOOL值,当为YES时,会隐藏上方原本推送的内容视图,只会显示我们自定义的视图。(默认为NO)
二、通知代码
1、通知注册
func registerAPNS() -> Void{
let sysVer = (UIDevice.current.systemVersion as NSString).floatValue
if (sysVer >= 10) {
// iOS 10
if #available(iOS 10.0, *){
registerPush10()
}
} else if (sysVer >= 8) {
// iOS 8-9
registerPush8to9()
} else {
// before iOS 8
registerPushBefore8()
}
}
@available(iOS 10.0, *)
func registerPush10() -> Void{
let center = UNUserNotificationCenter.current()
center.delegate = self
center.requestAuthorization(options: [.alert, .badge, .sound]) { (result, error) in
if result == true{
print("注册成功")
}
}
//添加UNNotificationAction(代码见上面UNNotificationAction介绍)
ios10ActionRegister()
UIApplication.shared.registerForRemoteNotifications()
}
func registerPush8to9() -> Void{
//添加UNNotificationAction(代码见上面UNNotificationAction介绍)
let category = ios8To9ActionRegister()
let mySettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: [category])
UIApplication.shared.registerUserNotificationSettings(mySettings)
UIApplication.shared.registerForRemoteNotifications()
}
func registerPushBefore8() -> Void{
//UIApplication.shared.registerForRemoteNotifications(matching: [.alert, .badge, .sound])
}
然后调用代理方法,运用第三方推送服务腾讯信鸽(如果自己服务端实现推送,可以将信鸽注册转化成本地服务器注册)
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
//信鸽平台注册device token
let deviceTokenStr = XGPush.registerDevice(deviceToken, account:"lxl125z" , successCallback: {
print("😀😀信鸽平台注册成功!")
}) {
print("😞😞信鸽平台注册失败!")
}
print("device token is \(String(describing: deviceTokenStr))")
}
//注册APNs失败调用的方法
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("😞😞注册APNs失败,reason : \(error)")
}
2、通知代理回调
ios10回调方法
@available(iOS 10, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
//点击通知的处理(即使应用在未打开的状态,点击通知栏进入,也会走此方法)
//ios10以上,本地通知和远程通知合并,都会走上面的两个代理方法,可根据trigger去判断
localOrAPNsPushHandel(notification: response.notification)
completionHandler()
}
@available(iOS 10, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
//应用在前台的时候显示通知栏时会调用
//设置应用在前台显示通知栏
completionHandler([.alert, .badge, .sound])
}
@available(iOS 10.0, *)
func localOrAPNsPushHandel(notification: UNNotification){
if notification.request.trigger is UNPushNotificationTrigger{
let content = notification.request.content
//处理action((代码见上面UNNotificationAction介绍))
ios10HandelActionButton(response: response)
if UIApplication.shared.applicationState != .active{
print("【ios10设备】点击远程通知\n",content.subtitle,content.title,content.body)
}else{
print("【ios10设备】前台接到通知")
}
}else{
//处理action(代码见上面UNNotificationAction介绍)
ios10HandelActionButton(response: response)
print("处理本地推送")
}
}
ios10以下设备的回调
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
//如果适配iOS6需要这个方法,【如果同时实现'didReceiveRemoteNotification completionHandler:'方法,会忽略当前方法,执行下面的方法】
print("【ios6设备】接到【远程推送】通知")
clickStatistic(userInfo: userInfo) //统计通知点击量
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
//收到远程通知的回调(基于iOS 7 及以上的系统版本,如果是使用 iOS 7 的 Remote Notification 特性那么处理函数需要使用这个的方法,可以收到静默推送的回调)
print("😜😜接到【通知推送】")
clickStatistic(userInfo: userInfo) //统计通知点击量
completionHandler(.newData)
}
//收到本地推送的回调
func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
//本地通知
}
//处理action
func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, for notification: UILocalNotification, completionHandler: @escaping () -> Void) {
//ios8to9 处理本地通知Action
}
func application(_ application: UIApplication, handleActionWithIdentifier identifier: String?, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: @escaping () -> Void) {
//ios8to9 处理远程通知Action
}
3、本地通知
ios10以上设备
可以发送多媒体通知
@available(iOS 10, *)
func localNotification_iOS10_After(){
let content = UNMutableNotificationContent()
content.title = "【本地推送】标题"
content.subtitle = "【本地推送】副标题"
content.body = "【本地推送】主题内容"
content.badge = 1
//设置声音
let sound = UNNotificationSound.default()
content.sound = sound
//设置通知附件内容(可以设置视频,图片,语音),创建附件的url,必须是一个文件路径,才能获取文件路径,开头是file://
let filePath = Bundle.main.path(forResource: "notification_audio", ofType: "mp3")
//let filePath = Bundle.main.path(forResource: "notification_image", ofType: "png")
let fileUrl = URL(fileURLWithPath: filePath!)
let att = try! UNNotificationAttachment(identifier: "show_audio", url: fileUrl)
content.attachments = [att]
content.categoryIdentifier = "categoryIdentifier"
//修改展示的lanchImage
content.launchImageName = "LaunchImage_1"
//触发模式(大于60s才能设置repeats)
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
// 设置UNNotificationRequest
let requestIdentifer = "TestRequest";
let request = UNNotificationRequest(identifier: requestIdentifer, content: content, trigger: trigger)
//添加UNNotificationAction
let enterAction = UNNotificationAction(identifier: "enterApp", title: "打开应用", options: .foreground)
let ingnoreAction = UNNotificationAction(identifier: "ingnoreApp", title: "忽略", options: .destructive)
let category = UNNotificationCategory(identifier: "categoryIdentifier", actions: [enterAction,ingnoreAction], intentIdentifiers: [], options: UNNotificationCategoryOptions(rawValue: 0))
UNUserNotificationCenter.current().setNotificationCategories([category])
//把通知加到UNUserNotificationCenter, 到指定触发点会被触发
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
ios10以下设备
func localNotification_iOS10_Before(){
let localNoti = UILocalNotification()
localNoti.fireDate = Date(timeInterval: 10, since: Date())
localNoti.timeZone = NSTimeZone.default
localNoti.alertBody = "【本地推送】主题内容"
if #available(iOS 8.2, *) {
localNoti.alertTitle = "本地推送】标题"
}
localNoti.alertLaunchImage = "LaunchImage_1"
localNoti.soundName = UILocalNotificationDefaultSoundName
localNoti.alertAction = "打开应用"
localNoti.applicationIconBadgeNumber = 1
UIApplication.shared.scheduleLocalNotification(localNoti)
}
4、远程通知
ios10以上设备可以通过NotificationExtention接收多媒体消息通知,ios10之下无此功能
Notification Extention Service
中可以对收到的通知进行重新组装,以下是两个代理方法
//这是处理通知内容重写的方法
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
print(request.content.userInfo)
if let bestAttemptContent = bestAttemptContent {
// 重写一些东西
bestAttemptContent.title = "【APNs推送】标题";
bestAttemptContent.subtitle = "【APNs推送】子标题";
bestAttemptContent.body = "【APNs推送】body";
let dict = bestAttemptContent.userInfo
let apsDict = dict["aps"] as! [AnyHashable : Any]
if let category = apsDict["category"] as? String{
bestAttemptContent.categoryIdentifier = category
}
// 根据通知返回的字段,获取文件的URL
guard let fileUrl = dict["fileUrl"] as? String,let fileType = dict["fileType"] as? String else{
contentHandler(bestAttemptContent)
return
}
//下载文件
print("在此处做下载处理,成功后调用contentHandler(bestAttemptContent)",fileUrl)
}
override func serviceExtensionTimeWillExpire() {
//附件发送通知失败,调用这个
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
Notification Extention content
可以对界面进行重新定制
// 这个方法是说,只要你收到通知,并且保证categoryIdentifier的设置,跟info.plist里面设置的一样,你就会调用这个方法。注意:一个会话的多个通知,每个通知收到时,都可以调用这个方法。
func didReceive(_ notification: UNNotification) {
self.label?.text = notification.request.content.body
}
//其他的一些代理方法。可以对播放按钮进行定制
网友评论