APNs Push Notification教程一

语言: swift, 版本:4.2,XCode:10.1
写作时间:2018-12-14

说明Push的作用

Push的作用是提醒用户,你要用俺开发的APP啦,提升日活和使用频率的神器。无论APP是在后台,还是进程已经被结束了,只要用户手机联网即可。比如你要少用微信,关掉微信的推送试试(当然卸载更绝)。

Push的工作流程如下:
Provider Server是发送Payload内容 (其实是Json,包含device、APP、内容信息)到APNs (Apple负责推送消息的服务器),只要你的iPhone在线,那么就一直跟APNs保持长连接。所以一有消息过来,iPhone就会显示推送消息。
APNs Push Notification教程一_第1张图片
带着下面的问题往下看:

  1. Provider怎么知道给那台设备推送?

Push显示给用户的形态

iOS 12,Push notifications可以做以下事情:

  • 显示简短文字.
  • 播放推送声音.
  • 显示消息数字在icon上面.
  • 在消息上提供交互,不需要打开APP. 比如点赞、回复.
  • 显示媒体附件,比如图片,GIF.
  • 静默推送,后台刷新APP的内容,前提是APP进程存在.
  • 消息分组.
  • 编辑或者移除推送.
  • 在展示消息前,内容可以被编辑.
  • 消息显示定制化UI.

此教程例子必要的条件

完成教程,需要下面的条件方能测试:

  • 真机iPhone、iPad、iTouch等移动端设备,Push notifications 不能在模拟器上面使用.
  • 开通Apple开发者账号,需要配置证书、Profile,生成Provider需要的秘钥等. (总不能允许任何人都可以给自己的APP发消息吧?)
  • 下载Mac客户端模拟Provider的角色,发送payload到APNs.

Push的工作流程

Provider发送,APP接收push notifications, 你只要完成下面三件事:

  1. Push配置:配置证书,Project配置,注册Device到Apple Push Notification service (APNs).
  2. Provider Server发送一条push notification 到APNs, APNs自动发送push notification给设备.
  3. APP消费掉push notification的内容,一般是在delegate里面回调(callback).

Push的证书配置

证书配置是为了保证用户下载的是正版的APP,Push证书是为了保证Provider是对应自己APP的才有权限推送。
为了便于理解,从零开始演示配置的信息,网页打开开发者首页https://developer.apple.com,–》Account, --》 Certificates, Identifiers & Profiles

  1. 先配置App IDs, 新建
    APNs Push Notification教程一_第2张图片

  2. 推送不能用通配符,点击最下面的Continue按钮,下一页点击最下面Register按钮,下一页点击最下面的Done按钮。
    APNs Push Notification教程一_第3张图片

  3. 点击Certificates, IDs & Profiles -> Identifiers -> App IDs 选择刚刚建立的id --> com.zgpeace.demo,
    APNs Push Notification教程一_第4张图片

  4. 你可以看到服务列表Application Services available, 其中 Push Notifications显示的是可配置的configurable:
    在这里插入图片描述

  5. 点击最下面的Edit按钮,找到Push Notification,左边打勾APNs Push Notification教程一_第5张图片

  6. 这里创建Development SSL Certificate --》 点击Create Certificate. (Production SSL Certificate的创建是类似的). --》 打勾 Apple Push Notification service SSL(Sandbox) --> 点击Continue
    APNs Push Notification教程一_第6张图片

  7. 选择创建的App ID com.zgpeace.demo, 点击Continue。这里会明确说明,不能用通配符的App ID才能创建Apple Push Notification service SSL certificate.
    APNs Push Notification教程一_第7张图片

  8. 创建Certificate Signing Request(CSR). 这个界面是显示如何创建CSR的步骤。下面会一步一步创建CSR,这个时候要开小差了,不着急点Continue哦。(下面以8.?表示分步骤,待到9.才是点Continue下来的。)
    APNs Push Notification教程一_第8张图片

8.1. 打开Keychain Access软件,路径为 Application folder --> Utilities folder --> 打开 Keychain Access. 或者用快键键CMD + Space打开Spotlight,输入Keychain Access。

8.2. 点击Keychain左上角的下拉按钮, 选择 Keychain Access > Certificate Assistant > Request a Certificate from a Certificate Authority.
注意:这里有个尿点,很多人采坑,注意Category 一定要选择Certificates,否则出各种诡异的错误。
APNs Push Notification教程一_第9张图片
APNs Push Notification教程一_第10张图片

8.3. 在Certificate Information窗口, 填写下面的信息:

  • User Email Address field填写你的邮箱.
  • Common Name field填写一个私钥的名字 (e.g., John Doe Dev Key).
  • CA Email Address field要留空.
  • “Request is” 分组, 选择"Saved to disk" .
  • 点击Continue,完成CSR生成, 保存到本地,这个马上要用,保存到可以找到的地方.
    APNs Push Notification教程一_第11张图片
  1. 点击8的Continue,选择8.3生成CSR文件,点击Continue
    APNs Push Notification教程一_第12张图片

  2. 如果一切顺利,将会得到证书页面。下载证书,安装。
    APNs Push Notification教程一_第13张图片

  3. 安装成功以后,可以在Keychain Access找到证书信息。
    在这里插入图片描述

  4. 恭喜你!这个步骤很长,但是值得。查看App ID的Development证书状态,已经变为Enabled,路径为 Certificates, IDs & Profiles -> Identifiers -> App IDs Push Notifications :在这里插入图片描述

  5. 上面生成的证书是给Provider Server用的。 因为是新创建的App ID, project需要运行的话,需要先建立客户端的Development证书(CSR可以用之前的),接在在设备里面加入iPhone的DeviceId,证书跟DeviceId一起创建Profile。
    APNs Push Notification教程一_第14张图片
    证书实际上是包含公钥跟私钥,加密以及身份验证用的;Device Id表明哪些设备可以用于调试; Profile表示档案,最终打包上传App Store,Apple根据这些信息校验App是否是正版。

因为已经跑题,罗列了一下结果图。

Development Certificate 创建结果图, 下载并安装证书 :
APNs Push Notification教程一_第15张图片
Device Id添加结果图APNs Push Notification教程一_第16张图片

Profile生成后,点击下载,双机安装,就装到XCode里面去了。
APNs Push Notification教程一_第17张图片

Push的demo工程启动

下载demo工程https://github.com/zgpeace/WenderCast-Starter,运行结果图

APNs Push Notification教程一_第18张图片

Push的project配置

修改Bundle Identifier为新建com.zgpeace.demo, WenderCast target > General > change Bundle Identifier
APNs Push Notification教程一_第19张图片

开启Push Notification, WenderCast target > the Capabilities tab > Push Notifications 选择ON:
在这里插入图片描述

Push的权限

iPhone的体验很好,做任何事情都要经过用户同意才能处理。
推送也一样,第一步先征得用户是否需要推送这个功能。

  1. 打开文件 AppDelegate.swift 在文件的最上面加上:
import UserNotifications
  1. 加下面的方法在 AppDelegate的最下面:
func registerForPushNotifications() {
  UNUserNotificationCenter.current() // 1
    .requestAuthorization(options: [.alert, .sound, .badge]) { // 2
      granted, error in
      print("Permission granted: \(granted)") // 3
  }
}

分析上面代码:

  1. UNUserNotificationCenter 处理了APP所有推送相关的事件.
  2. requestAuthorization(options:completionHandler:) 请求授权用推送的权限. options 表示推送可以展示的情况 – 例子里设置了 alert, sound and badge.
  3. 授权结果通过Bool得知.

注解: options 参数 requestAuthorization(options:completionHandler:) 可以是下面的任何组合 UNAuthorizationOptions:
.badge: 显示推送书在 app’s icon.
.sound: 播放声音.
.alert: 显示文字.
.carPlay: 显示推送在车载系统.
.provisional: 发布不会被拒绝的推送. 比如静默推送.
.providesAppNotificationSettings: 表示App有自己的推送设置UI.
.criticalAlert: 忽略静音,并且不会被打断。你需要向Apple申请者特殊的权利, 并说明这是必要的. .

  1. 在方法application(_:didFinishLaunchingWithOptions:)的末尾,在 return之前加入以下代码:
registerForPushNotifications()

构建 > 运行。当APP运行起来后,弹框问用户是否允许发推送。
APNs Push Notification教程一_第20张图片

  1. 用户点击允许,App可以显示推送了。真棒!但是,要是用户拒绝了呢?加下面的方法在 AppDelegate:
func getNotificationSettings() {
  UNUserNotificationCenter.current().getNotificationSettings { settings in
    print("Notification settings: \(settings)")
  }
}
  1. registerForPushNotifications, 用下面替换掉方法 requestAuthorization(options:completionHandler:) :
UNUserNotificationCenter.current()
  .requestAuthorization(options: [.alert, .sound, .badge]) {
    [weak self] granted, error in
      
    print("Permission granted: \(granted)")
    guard granted else { return }
    self?.getNotificationSettings()
}

Push的token获取

用户授权成功后,接下来获取Push的token,需要发送给Provider Server,存入数据库。

  1. 在getNotificationSettings(), 在closure的里面print的下面,加以下代码:
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
  UIApplication.shared.registerForRemoteNotifications()
}
  1. 获取token成功print出来,后面会用到。出错,打印出错信息。
    以下方法为成功、失败的delegate。
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)")
}

token例子:
在这里插入图片描述

Push Server的配置

  1. 创建Authentication Key,你只要创建一个key,你所用应用都可以用。
    网页打开开发者首页https://developer.apple.com,–》Account, --》 Certificates, Identifiers & Profiles --》Keys ▸ All。在右上角, 点击 加号+按钮 .

取个名字, 比如 Push Notification Key. 在 Key Services 下面, 勾选 Apple Push Notifications service (APNs).
APNs Push Notification教程一_第21张图片

点击 Continue ,接着 Confirm 在下个页面,点击下载. 文件名字类似于 AuthKey_4SVKWF966R.p8. 保存好该文件,你需要用它来发推送! 4SVKWF966R 文件名字的一部分是Key ID. 你也需要它.

你最后需要的是your Team ID. 点击链接跳转到Membership Details 页面,你就会找到.

唷!配置的道路好长,接下来就可以发送推送信息了。

Push Server发送payload推送

点击链接下载 PushNotifications .

打开 PushNotifications,并配置下面的信息:

  1. 在 Authentication下面, 选择 Token.
  2. 点击按钮 Select P8 ,选择 文件 ** .p8** 在前面的有讲解.
  3. 输入 Key ID 和 Team ID.
  4. Body下面, 输入app的 Bundle ID 和 device token.
  5. 把request body的内容改为如下:
{
  "aps": {
    "alert": "Breaking News!",
    "sound": "default",
    "link_url": "https://raywenderlich.com"
  }
}
  1. app退到background,或者锁屏.
  2. 点击按钮Send button在 PushNotifications.
    APNs Push Notification教程一_第22张图片

你可以获取到自己的第一个推送:
APNs Push Notification教程一_第23张图片

Payload JSON说明

payload就是JSON,必须包含的key是 aps, 它也是个字典 dictionary.

apt预置了7对keys,以下是具体说明:

  • alert: 可以试字符串string, 也可以是字典dictionary. 作为 dictionary, 它可以国际化文字或者改变通知的样子,类似于CSS(猜的,不确定对不对).
  • badge: 显示推送书在icon的右上角. 去掉数字显示,设置为0即可.
  • sound: 声音预置在app里面. 定制的声音要小于30秒,还有一些限制(细节要看官方文档了).
  • thread-id: 对推送消息分组.
  • category: 给推送消息分类, 用于定制化相应推送. 接下来有栗子?.
  • content-available: 设置这项为1, 推送就是静默推送. 在下面你将学到静默推送.
  • mutable-content: 设置这项为1, app可以先显示之前修改内容.

除了上面预置的keys, 你可以加其它字段,只要payload小于 4,096 bytes.

APP处理,Server发过来的push

处理推送通知都在UIApplicationDelegate类的delegate里面,根据APP所处的状态分为两类:

  • 如果APP进程已经结束,当点击推送消息,相应方法是application(_:didFinishLaunchingWithOptions:).
  • 如果APP运行在前台或者后台,系统回调的方法是application(_:didReceiveRemoteNotification:fetchCompletionHandler:). 如果用户点击推送消息,iOS会再次调用该方法, 你可以更新UI和显示相关信息.
  1. 第一种情况(进程已经结束), WenderCast 会建立新的消息子项, 并打开News tab. 在方法application(_:didFinishLaunchingWithOptions:)的末尾在 return 之前加上一下代码:
// Check if launched from notification
let notificationOption = launchOptions?[.remoteNotification]

// 1
if let notification = notificationOption as? [String: AnyObject],
  let aps = notification["aps"] as? [String: AnyObject] {
  
  // 2
  NewsItem.makeNewsItem(aps)
  
  // 3
  (window?.rootViewController as? UITabBarController)?.selectedIndex = 1
}

上面代码说明:

  1. 检查UIApplication.LaunchOptionsKey.remoteNotification 是否存在. 如果存在,则说明APP是点击推送唤醒的. 这就是payload的内容.
  2. 如果是推送,则捕获aps dictionary,并创建NewsItem.
  3. 切换tab到News栏目.

调试APP进程不存在的情况,需要修改Scheme:
APNs Push Notification教程一_第24张图片

点击WenderCast scheme并选择编辑 Edit Scheme…. 左侧栏选择Run , 接着在Info tab 选择 Wait for executable to be launched:
APNs Push Notification教程一_第25张图片

这个设置使调试在等待,第一次点击推送消息才唤醒APP.

构建并运行. 当APP安装好后, 发送多个推送. 点击推送,APP就好打开news tab:
APNs Push Notification教程一_第26张图片

  1. 第二种情况,APP在前台或者后台(进程存在)。
    处理进程存在的APP,在AppDelegate 增加下面的方法:
func application(
  _ application: UIApplication,
  didReceiveRemoteNotification userInfo: [AnyHashable: Any],
  fetchCompletionHandler completionHandler:
  @escaping (UIBackgroundFetchResult) -> Void
) {
  guard let aps = userInfo["aps"] as? [String: AnyObject] else {
    completionHandler(.failed)
    return
  }
  NewsItem.makeNewsItem(aps)
}

这个方法通过push message创建了一个新NewsItem.

在进程存在的情况下,这个方法会调用。修改 scheme 回到 launching > automatically. 路径为:WenderCast scheme > Edit Scheme > Run > Info tab > automatically.
APNs Push Notification教程一_第27张图片

构建Build并运行run. app运行z在前台并停留在News tab. 发送推送通知可以看到消息item在增加:
APNs Push Notification教程一_第28张图片

That’s it! 你的app现在可以神奇地接收推送消息了.

定制Push的相应事件

显示Actionable notifications

Actionable notifications 可以加定制化按钮在推送消息里面,比如可以看到email APP显示回复按钮,Tweets显示点赞按钮.

在方法registerForPushNotifications()里面, 在guard下面,getNotificationSettings()的上面加入以下方法:

// 1
let viewAction = UNNotificationAction(
  identifier: viewActionIdentifier, title: "View",
  options: [.foreground])

// 2
let newsCategory = UNNotificationCategory(
  identifier: newsCategoryIdentifier, actions: [viewAction],
  intentIdentifiers: [], options: [])

// 3
UNUserNotificationCenter.current().setNotificationCategories([newsCategory])

代码说明:

  1. 创建新的notification action, title View在按钮上. 这个事件有唯一的标识, 区分不同的事件处理.
  2. 定义news category, 它包含了上面的view action. 它有区分的标识符"newsCategory" 在payload里面, 区分不同的category.
  3. 最后,调用 setNotificationCategories, 注册 actionable notification.

That’s it! 构建并运行 app去注册新的 notification settings.

app推到后台,接着用PushNotifications发送下面的 payload :

{
  "aps": {
    "alert": "Breaking News!",
    "sound": "default",
    "link_url": "https://raywenderlich.com",
    "category": "NEWS_CATEGORY"
  }
}

如果一切顺利,下拉推送通知,你可以看到下面的结果View action:
APNs Push Notification教程一_第29张图片

Nice! 点击会唤醒WenderCast, 但是它没有任何响应事件. 为了使其调整的News tab,需要完善delegate.

处理Actionable notifications的Action

当触发了notification action, UNUserNotificationCenter 通知 delegate. 在文件AppDelegate.swift的最下面, 增加 class extension:

extension AppDelegate: UNUserNotificationCenterDelegate {
  func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse,
    withCompletionHandler completionHandler: @escaping () -> Void) {
    
    // 1
    let userInfo = response.notification.request.content.userInfo
    
    // 2
    if let aps = userInfo["aps"] as? [String: AnyObject],
      let newsItem = NewsItem.makeNewsItem(aps) {
      
      (window?.rootViewController as? UITabBarController)?.selectedIndex = 1
      
      // 3
      if response.actionIdentifier == viewActionIdentifier,
        let url = URL(string: newsItem.link) {
        let safari = SFSafariViewController(url: url)
        window?.rootViewController?.present(safari, animated: true,
                                            completion: nil)
      }
    }
    
    // 4
    completionHandler()
  }
}

custom action唤醒APP后,callback做了很熟悉的解析payload的操作:

  1. 获取aps dictionary.
  2. 创建NewsItem,并跳转到News tab.
  3. 检查action identifier, 是否存在identifier. 如果是 “View” action并且链接是有效的, 它会展示显示链接页面在 SFSafariViewController.
  4. 调用系统的completion handler .

最后需要设置delegate为UNUserNotificationCenter. 在application(_:didFinishLaunchingWithOptions:) 最上面加下面的代码:

UNUserNotificationCenter.current().delegate = self

Build and run. 再次结束app的进程, 接着发送推送用下面的payload:

{
  "aps": {
    "alert": "New Posts!",
    "sound": "default",
    "link_url": "https://raywenderlich.com",
    "category": "NEWS_CATEGORY"
  }
}

下拉 notification并点击 View action,你可以看到WenderCast present 显示Safari View controller, 当APP启动以后:
APNs Push Notification教程一_第30张图片

Congratulations, 你已经实现了actionable notification!

静默推送

当数据库有新数据的时候,发个静默推送(Silent Push Notifications), 后台帮用户更新数据就好。 这样的好处是,不用客户端间断性轮询更新数据。

需要在Background Modes开启 Remote notifications, 路径为 WenderCast target > Capabilities tab > Background Modes (打开) > Remote notifications(勾选):
APNs Push Notification教程一_第31张图片

现在,APP可以在后台,获取到静默推送了

在类 AppDelegate中, 找到 application(_:didReceiveRemoteNotification:). 把 NewsItem.makeNewsItem() 替换为下面代码:

// 1
if aps["content-available"] as? Int == 1 {
  let podcastStore = PodcastStore.sharedStore
  // 2
  podcastStore.refreshItems { didLoadNewItems in
    // 3
    completionHandler(didLoadNewItems ? .newData : .noData)
  }
} else  {
  // 4
  NewsItem.makeNewsItem(aps)
  completionHandler(.newData)
}

解析代码:

  1. 检查 content-available 是否为 1, 是则表示silent notification.
  2. 异步刷新 podcast list.
  3. 当刷新结束调用completion handler 让系统更新数据(没新数据则不处理).
  4. 如果不是silent notification, 采取是新内容创建新的 news item.

build and run, App保持在前台foreground, PushNotifications推送下面的payload :

{
  "aps": {
    "content-available": 1
  }
}

静默推送,除非服务端返回新的数据,否则界面看不出变化,可以用调试的方式,看看走的逻辑是否符合预期。

总结

Congratulations! 你已经完成了推送的知识:证书配置、Project设置、Provider模拟推送、App处理推送payload、定制化按钮Actionable notifications、 静默推送!

代码在:https://github.com/zgpeace/WenderCast-Starter
分支说明:master是初始化代码, finish是按照上面的例子完成的。
真机测试,需要修改所有的BundleId, Certificate, Profile。

感谢阅读:如有任何问题,请留言,谢谢!

参考文章:
https://www.raywenderlich.com/8164-push-notifications-tutorial-getting-started
https://medium.com/flawless-app-stories/ios-remote-push-notifications-in-a-nutshell-d05f5ccac252
https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html

你可能感兴趣的:(iOS)