iOS推送的实现

iOS中的推送分为本地推送和远程推送,本文主要介绍远程推送的原理。

原理

还是先从下面这张经典的图说起:

要将指定消息推送到指定的设备上,首先明确谁来发送这个消息,在iOS的推送中,最后实际发送消息的就是APNS-苹果的推送服务器,APNS只是帮忙发送消息,具体发的消息内容以及发到哪些设备上这些都需要额外的服务器告诉APNS,也就是需要部署自己的推送服务器和APNS对接,实际开发中经常使用第三方平台的推送服务诸如友盟,极光和个推等;其次需要确定的是APNS如何定位到指定的设备(即只给指定的设备推送消息),而且推送的消息点击后打开指定的App(我们自己开发的)?这里就需要Device Token这个身份证,有了这个身份证APNS就能准确的将消息推送到指定的设备上。

  • 首先在项目中书写代码请求注册推送,这一步会弹窗告知用户让用户授权。
  • 用户授权成功后,系统会向APNS服务器请求Device Token
  • App在拿到这个Device Token后,要将这个标识自己身份的Device Token上送到第三方服务器集中管理。
  • 当有消息需要推送时,利用第三方服务器的推送窗口,将消息以及需要发送的Device Token信息一并发送给APNS,最后APNS会将消息推送到指定的设备上。

项目配置推送环境

Signing & Capabilities点击Capability button添加Push Notifications,需要开发证书里包含了推送功能。

请求用户授权

一般在application(_:didFinishLaunchingWithOptions:) 中注册推送通知,需要在主线程中执行。

func registerForPushNotifications() {
  UNUserNotificationCenter.current() 
    .requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in
      guard granted else { return }
     // 需要检查设置中的权限,因为用户随时可能关闭权限
      self?.getNotificationSettings()
    }
}
func getNotificationSettings() {
  UNUserNotificationCenter.current().getNotificationSettings { settings in
     guard settings.authorizationStatus == .authorized else { return }
      DispatchQueue.main.async {
        UIApplication.shared.registerForRemoteNotifications()
      }
  }
}

.options参数:

.badge:在图标右上角显示数字
.sound: 播放声音
.alert: 弹窗显示文本通知
.carPlay: 在CarPlay上显示通知
.provisional: 没有提醒的通知。不会有弹窗授权,但你的通知只会默默地显示在通知中心
.providesAppNotificationSettings: 表示该应用有自己的通知设置的用户界面
.criticalAlert: 忽略静音开关和请勿打扰。你需要从苹果那里获得一个特殊的权利来使用这个选项,因为它只用于非常特殊的使用情况。

处理APNS回调

在向APNS注册推送成功后,会返回当前设备上此APP对应的Device Token

  func application(
    _ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
  ) {
    let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
    let token = tokenParts.joined()
    print("Device Token: \(token)")
  }
  func application(
    _ application: UIApplication,
    didFailToRegisterForRemoteNotificationsWithError error: Error
  ) {
    print("Failed to register: \(error)")
  }
}

APNS推送消息时,用户点击消息后在以下代理方法中进行处理:

extension AppDelegate: UNUserNotificationCenterDelegate {
  func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse,
    withCompletionHandler completionHandler: @escaping () -> Void
  ) {
    let userInfo = response.notification.request.content.userInfo
   // 处理推送消息
    completionHandler()
  }
}

使用xcode在模拟器上模拟通知

由于消息需要通过第三方服务器转发给APNS服务器,这里采用Xcode模拟发送通知,首先查看模拟器的device_identifier,点击Windows ▸ Devices and Simulators切换到Simulators选择当前的模拟器拷贝Identifier


创建一个名为first.apn的文件,内容如下:

{
  "aps": {
    "alert": "Breaking News!",
    "sound": "default",
    "link_url": "https://www.baidu.com"
  }
}

cdfirst.apn文件的目录下,输入指令xcrun simctl push device_identifier bundle_identifier first.apn,其中device_identifierbundle_identifier替换为刚刚获取的模拟器的device_identifier和项目的Bundle_identifier,例如如下所示:

这里Xcode利用模拟器的device_identifie结合Bundle_identifier来确定给哪个模拟器的哪个App发送消息,相当于上面提到的Device Token

模拟真机上推送

在真机上推送消息时,第三方服务器是需要苹果的推送证书和APNS服务器交互的,推送证书分为开发者推送证书和生产推送证书,同时有p12p8二种格式的,具体证书的申请流程不在此赘述。

App store中下载APNS Tool软件,软件打开如下:


选择Credentials,点击Request authotization申请授权,此时下面的CREDENTIALS会显示相关信息包括Device token等,拷贝下面的P8 key字符串,这其实就是p8证书的内容,利用拷贝的内容建立一个名为push.p8的推送证书文件(新建纯文本文件粘贴后修改文件后缀即可)。最后将Credentials页面的内容全部复制粘贴到Push页面上,顺便将APNs Server切换至Production,点击右上角的Send push即可收到推送。

要想在另外一台手机上收到推送,只需要输入另外一台手机的Device Token即可;同时测试推送的软件也可以使用Mac端PushNotifications软件。

第三方平台个推的实现

本文选取第三方个推作为介绍,其他平台诸如友盟,极光推送等使用方式大同小异,官方Demo地址和官方视频讲解地址。

SDK的集成

在集成SDK前需要去个推官网创建应用并上传证书拿到AppIdAppKeyAppSecret,直接下载官方SDK集成即可。

通过SDK注册推送

集成了SDK后一切操作都变得十分简单,首先是启动SDK,填入上面所说的AppIdAppSecretAppKey

    // [ GTSDK ]:使用APPID/APPKEY/APPSECRENT启动个推
    GeTuiSdk.start(withAppId: kGtAppId, appKey: kGtAppKey, appSecret: kGtAppSecret, delegate: self)

通过SDK注册远程推送通知,使用SDK后无需关注DeviceToken相关逻辑(SDK都帮忙做了),只需要关系消息回调即可。

GeTuiSdk.registerRemoteNotification([.alert, .badge, .sound])

个推消息回调

通过回调拿到个推消息。

  func geTuiSdkDidReceiveNotification(_ userInfo: [AnyHashable : Any], notificationCenter center: UNUserNotificationCenter?, response: UNNotificationResponse?, fetchCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)? = nil) {
    let msg = "[ TestDemo ] \(#function) \(userInfo)"
    homePage.logMsg(msg)
    // [ 参考代码,开发者注意根据实际需求自行修改 ]
    completionHandler?(.noData)
  }
   // 透传消息回调
  func geTuiSdkDidReceiveSlience(_ userInfo: [AnyHashable : Any], fromGetui: Bool, offLine: Bool, appId: String?, taskId: String?, msgId: String?, fetchCompletionHandler completionHandler: ((UIBackgroundFetchResult) -> Void)? = nil) {
    let msg = "[ TestDemo ] \(#function) fromGetui:\(fromGetui ? "个推消息" : "APNs消息") appId:\(appId ?? "") offLine:\(offLine ? "离线" : "在线") taskId:\(taskId ?? "") msgId:\(msgId ?? "") userInfo:\(userInfo)"
    homePage.logMsg(msg)
  }

在个推开发者平台上推送消息

在个推官网创建一个透传消息,填写通知内容,确认后发送:


image.png

总结

本文阐述了iOS推送的实现原理,其中如何获取DeviceToken最为重要,并通过相关推送App等辅助工具进行了推送的模拟,并介绍了第三方推送的使用(其他平台的使用大同小异),弄清了推送的原理后能更好的理解iOS的推送机制,通过APNS服务器推送保证了消息传递的准时性和统一性。

你可能感兴趣的:(iOS推送的实现)