iOS10 - 推送通知

iOS10里的通知与推送

通知文章2

远程推送工具

一、各版本通知比较

  1. iOS 8以后,APNs推送的字节是2k,iOS8以前是256字节,iOS10现在是4k
  2. iOS 9以后APNs支持HTTP/2协议栈,优化长连接,具有标准的HTTP返回和管道复用技术
  3. iOS 10以后,APNs可根据推送消息的唯一标示符查询某条消息是否被用户阅读,可更新某一推送消息,而不用发重读的多条消息,可推送多媒体消息

静默推送(iOS7之后):

应用收到通知后在后台(background)状态下运行一段代码,可用于从服务器获取内容更新;

静默推送:收到推送(没有文字没有声音),不用点开通知,不用打开APP,就能执行,用户完全感觉不到

- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo fetchCompletionHandler:(void(^)(UIBackgroundFetchResultresult))handler

推送格式:

  1. 一定不能加alert,如果加入了alert就不是静默推送了
  2. 一定要加"content-available" : 1
  3. sound也不能加,加入的话也是静默推送,可以测试时添加.

二、iOS10的通知

  1. iOS 10通知系统支持Images, GIFs, Audio and Video类型
  2. iOS 10推出Notification Service Extension与Notification Content Extension,可以实现推送数据在展示前进行下载更新、定制通知UI
  3. 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

iOS推送代码-地理位置

iOS10之后,通过2个代理方法来处理通知的接收和点击事件,此外,本地通知跟远程通知合二为一,区分本地通知跟远程通知的类是UNNotificationTrigger.hiOS10推送

  1. UNPushNotificationTrigger是APNs通知
  2. UNTimeIntervalNotificationTrigger(本地通知) 一定时间之后发送,可以设置重复(必须大于60s)
  3. UNCalendarNotificationTrigger(本地通知) 一定日期之后,可以设置重复,也就是下一相同间隔内也推送
  4. 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作用:

  1. 我们可以把即将给用户展示的通知内容,做各种自定义的处理,最终,给用户呈现一个更为丰富的通知。
  2. 安全性(收到通知后的最多30s内,你可以把你的通知内容,解密后,在重新展示在用户的通知拦上)
附件大小

11、UNNotificationContentExtension

iOS10_UNNotificationContentExtension 作者 徐不同)

收到远程或者本地通知的时候,弹出一个自定义界面

UNNotificationContentExtension的Info.Plist

info.plist

UNNotificationExtensionCategory(必须要有)

就是在收到通知的时候,我们可以让服务器把这个通知的categoryIdentifier带上,可以通过设置UNMutableNotificationContentcategoryIdentifier相同,关联当前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
    }
    
   //其他的一些代理方法。可以对播放按钮进行定制 
    
 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容

  • 本文发布在:iOS10 推送通知详解(UserNotifications) iOS10新增加了一个UserNoti...
    曲年阅读 9,146评论 2 22
  • iOS10新增加了一个UserNotificationKit(用户通知框架)来整合通知相关的API,UserNot...
    AlanGe阅读 471评论 0 0
  • 这篇文章整理iOS10之后的推送通知(文中的推送通知,如不做特殊说明,默认是iOS10以后的推送通知) iOS10...
    王技术阅读 3,519评论 5 10
  • 简介 新框架 获取权限 获取用户设置 注册APNS,获取deviceToken 本地推送流程 远程推送流程 通知策...
    liuyanhongwl阅读 20,399评论 35 51
  • 阿菱:好大的雨呀。 阿球:来也冲冲,去也匆匆。多姿多彩,滑盖缤纷。 阿菱:别站着说话不腰疼,还不都是为了生活?哎,...
    云水坡头阅读 271评论 0 0