推送的简单梳理

最近推送一直有一些问题,app icon的badge显示问题,推送接收处理问题等,发现自己一直不太清楚整个流程,于是从头梳理了一遍,助于理解记忆。

一、推送接入

application(_:didFinishLaunchingWithOptions:) 中加入如下代码

  if #available(iOS 10.0, *) {
    // version >= iOS 10
    let center = UNUserNotificationCenter.current()
    center.delegate = self
    var options: UNAuthorizationOptions = [UNAuthorizationOptions.alert, .sound, .badge]
    if #available(iOS 12.0, *) {
      // iOS12 新增加了 providesAppNotificationSettings,可视需求添加
      options = [.alert, .sound, .badge, .providesAppNotificationSettings]
    }
    // 弹出 允许授权弹窗
    center.requestAuthorization(options: options) { granted, error in
      if granted {
        // 用户允许进行通知
        center.getNotificationSettings{ (settings) in
          print("settings = \(settings)")
        }
      } else {
        // 用户点击了不允许
      }
    }
  } else {
    // iOS 8 <= version < iOS 10
    let userNotificationSettings : UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert,.sound,.badge], categories: nil)
    UIApplication.shared.registerUserNotificationSettings(userNotificationSettings)
  }
        
  // 注册获取 device Token
  UIApplication.shared.registerForRemoteNotifications()

在iOS12,UNAuthorizationOptions新增了criticalAlertprovidesAppNotificationSettingsprovisional,作用如下

  // 重要警报,可无视勿扰模式和铃声开关的限制进行提醒,适用于灾难预警等重要信息
  // 需要单独申请权限
  @available(iOS 12.0, *)
  public static var criticalAlert: UNAuthorizationOptions { get }

  // 在通知管理里直接进入app内部设置页面时使用(跳转需自己在代理方法中处理)
  // 代理方法为 userNotificationCenter(_:,openSettingsFor),后续会讲
  @available(iOS 12.0, *)
  public static var providesAppNotificationSettings: UNAuthorizationOptions { get }

  // 临时授权,通知会以隐式推送的方式推送,并带有两个权限按钮让用户自己选择之后正常推送还是关闭
  // 隐式推送:出现在通知中心并可在应用图标上出现标记,但不会显示在锁定屏幕上,不会显示横幅,也不会播放声音
  @available(iOS 12.0, *)
  public static var provisional: UNAuthorizationOptions { get }

二、推送处理

当一条推送到达手机的时候我们需要做一些处理,比如icon上的badge变动,点击推送时候的具体跳转等,这些处理的代理方法和其调用时机如下

  // 当content-available为1时可调用(即使app已被杀死),当silent_push = 1时为静默推送
  // 此方法因为可无视app是否存活调用,所以可用于推送撤销等行为,减少推送事故的发生(避免像36氪一样...),后续会讲
  func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    // 需要做的操作
    // xxxx
    completionHandler(.newData)
  }

  // app在前台时调用
  func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    // 需要做的操作
    // xxxx 
  }

  // 通知被点击后进入app时调用
  // 此方法内部可处理各个推送的action,后续会讲
  func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    // 需要做的操作
    // xxxx
  }

  @available(iOS 12.0, *)
  func userNotificationCenter(_ center: UNUserNotificationCenter!, openSettingsFor notification: UNNotification?) {
    if let trigger = notification?.request.trigger, trigger.isKind(of: UNPushNotificationTrigger.self) {
      // 从通知界面直接进入应用(通知左滑->管理->关闭...->在"xxx"中配置...->进入app)
      // 从图一入口进入调用
    }else{
      // 从通知设置界面进入应用(系统设置下面的通知页面下面的app通知页面进入app)
      // 从图二入口进入调用
    }
  }
图一
图二

ps:图上入口都是只有在添加providesAppNotificationSettings之后才会出现

如果接入的是jpush的话,jpush的下面几个代理方法会覆盖系统的方法,将只执行jpush的方法,不执行系统的方法

jpush代理方法

当app在前台运行时收到通知,系统会自动调用userNotificationCenter(_:willPresent:withCompletionHandler:),不过该条通知依旧会在通知中心显示,当用户在通知中心从该通知进入app时,会再次调用userNotificationCenter(_:didReceive:withCompletionHandler:),需要注意不要对通知做重复处理

三、进阶处理

Background 运行

content-available = 1时,系统可无视app存活状态调用application(_:didReceiveRemoteNotification:fetchCompletionHandler:)方法,在其它代理方法之前(需要在Xcode 中修改应用的 Capabilities 开启Remote notifications)

静默推送

silent_push = 1时,content-available必须为1,此时该条推送不会出现在用户任何可见区域,用户完全感知不到,badge也不会变化

推送覆盖

apns-collapse-id可用于推送覆盖,比如推送Aapns-collapse-id = 100发送成功之后,再次发送Bapns-collapse-id = 100,用户将会只在通知中心看到通知B(在用户未点击通知A的前提之下,参考微信的消息撤销功能)

推送撤销

效果:推送A发送成功之后,再次发送B去实现撤销功能,用户在通知中心将看不到A和B(在用户未点击通知A的前提之下)
实现:A为普通推送,B需要为静默推送,并传递需要撤销的通知id,然后在代理方法中处理

  func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    // 撤销通知
    if #available(iOS 10.0, *), let removeMessageId = userInfo["removeMessageId"] as? String {
      UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [removeMessageId])
    }
    completionHandler(.newData)
  }

你可能感兴趣的:(推送的简单梳理)