语言: 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就会显示推送消息。

带着下面的问题往下看:

  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, 新建

  2. 推送不能用通配符,点击最下面的Continue按钮,下一页点击最下面Register按钮,下一页点击最下面的Done按钮。

  3. 点击Certificates, IDs & Profiles -> Identifiers -> App IDs 选择刚刚建立的id --> com.zgpeace.demo,

  4. 你可以看到服务列表Application Services available, 其中 Push Notifications显示的是可配置的configurable:

  5. 点击最下面的Edit按钮,找到Push Notification,左边打勾

  6. 这里创建Development SSL Certificate --》 点击Create Certificate. (Production SSL Certificate的创建是类似的). --》 打勾 Apple Push Notification service SSL(Sandbox) --> 点击Continue

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

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

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,否则出各种诡异的错误。

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生成, 保存到本地,这个马上要用,保存到可以找到的地方.
  1. 点击8的Continue,选择8.3生成CSR文件,点击Continue

  2. 如果一切顺利,将会得到证书页面。下载证书,安装。

  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。

    证书实际上是包含公钥跟私钥,加密以及身份验证用的;Device Id表明哪些设备可以用于调试; Profile表示档案,最终打包上传App Store,Apple根据这些信息校验App是否是正版。

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

Development Certificate 创建结果图, 下载并安装证书 :

Device Id添加结果图

Profile生成后,点击下载,双机安装,就装到XCode里面去了。

Push的demo工程启动

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

Push的project配置

修改Bundle Identifier为新建com.zgpeace.demo, WenderCast target > General > change Bundle Identifier

开启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]) { // 2granted, error inprint("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运行起来后,弹框问用户是否允许发推送。

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

点击 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.

你可以获取到自己的第一个推送:

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] {// 2NewsItem.makeNewsItem(aps)// 3(window?.rootViewController as? UITabBarController)?.selectedIndex = 1
}

上面代码说明:

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

调试APP进程不存在的情况,需要修改Scheme:

点击WenderCast scheme并选择编辑 Edit Scheme…. 左侧栏选择Run , 接着在Info tab 选择 Wait for executable to be launched:

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

构建并运行. 当APP安装好后, 发送多个推送. 点击推送,APP就好打开news tab:

  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.

构建Build并运行run. app运行z在前台并停留在News tab. 发送推送通知可以看到消息item在增加:

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:

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) {// 1let userInfo = response.notification.request.content.userInfo// 2if let aps = userInfo["aps"] as? [String: AnyObject],let newsItem = NewsItem.makeNewsItem(aps) {(window?.rootViewController as? UITabBarController)?.selectedIndex = 1// 3if response.actionIdentifier == viewActionIdentifier,let url = URL(string: newsItem.link) {let safari = SFSafariViewController(url: url)window?.rootViewController?.present(safari, animated: true,completion: nil)}}// 4completionHandler()}
}

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启动以后:

Congratulations, 你已经实现了actionable notification!

静默推送

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

需要在Background Modes开启 Remote notifications, 路径为 WenderCast target > Capabilities tab > Background Modes (打开) > Remote notifications(勾选):

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

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

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

APNs Push Notification教程一相关推荐

  1. APNS(Apple Push Notification services)

    苹果的推送服务APNs基本原理简单来说就是苹果利用自己专门的推送服务器(APNs)接收来自我们自己应用服务器的需要被推送的信息,然后推送到指定的iOS设备上,然后由设备通知到我们的应用程序,设备以通知 ...

  2. pytorch打印模型参数_Pytorch网络压缩系列教程一:Prune你的模型

    Pytorch网络压缩系列教程一:Prune你的模型 本文由林大佬原创,转载请注明出处,来自腾讯.阿里等一线AI算法工程师组成的QQ交流群欢迎你的加入: 1037662480 深度学习模型取得了前所未 ...

  3. ×××送通知服务教程 Apple Push Notification Services Tutorial

    本文译自http://www.raywenderlich.com/.原文由iOS教程团队 Matthijs Hollemans 撰写,经原网站管理员授权本博翻译. 在iOS系统,考虑到手机电池电量,应 ...

  4. 推送技术 Push Notification

    为什么80%的码农都做不了架构师?>>>    1.BB,Nokia,Palm都先后支持了Push,目前流行的Push方式有三种. (1)短信触发:2G时代长时间的数据连接会影响电话 ...

  5. Apple Push Notification Service(苹果推送服务)

    https://developer.apple.com/library/IOS/documentation/NetworkingInternet/Conceptual/RemoteNotificati ...

  6. 怎样编写Apple Push Notification服务器

    http://www.iphone-geek.cn/%E7%BC%96%E7%A8%8B/%E6%80%8E%E6%A0%B7%E7%BC%96%E5%86%99apple-push-notifica ...

  7. push notification for iphone

    由于公司业务需求,以前一直做PHP开发,突然让我研究push notification ,一下子迷糊啦,不知所措,抓狂!但是在自己的努力下还是初有成效!现拿出来显摆一下! 1:push notific ...

  8. Push Notification (1)Google C2DM 服务

    基于Google C2DM的消息推送 前提: 1. Android设备上有Google服务(可以在'设置'->'正在运行'中看到),并且设置了gmail帐号和同步. 2. 开发者需要注册gmai ...

  9. AUTOCAD自学教程一

    AUTOCAD自学教程一 AUTOCAD自学教程 在学习任何一款软件的时候,第一步也是非常必要的一步就是如何的安装软件. 下面我们讲解如何的安装这个软件. 1.安装autocad 2.安装产品 3.正 ...

  10. 当用户流失比较明显后, 如何提升活跃度? push notification 是一个有效的方式吗?...

    当用户流失比较明显后, 如何提升活跃度? push notification 是一个有效的方式吗? 添加评论 分享 按票数排序 10 个回答 赞同反对,不会显示你的姓名 Linda Jiang,友盟商 ...

最新文章

  1. 大数据开发套件—调度运维常见问题
  2. Countly 19.02.1 发布,实时移动和 web 分析报告平台
  3. Latex、Lyx学习备忘
  4. STM32系统滴答定时器(systick)应用
  5. 释疑の采购Invoice常用参数设置
  6. linux 键盘记录,学习笔记 kali linux 关于MS17-101漏洞攻击与键盘记录
  7. 博客园如何使用MarkDown
  8. 怎么在win7链接无线网络连接服务器,Win7系统网络连接一直显示正在获取网络地址但是连不上网解决方法...
  9. 动态规划问题中最长公共子序列---C语言实现
  10. C#利用NI VAS采集图片
  11. pip3 安装pycrypto 时报错
  12. 关闭进程_Xbox Game Bar重磅更新:可不离开游戏关闭系统进程
  13. 【软考高级:信息系统项目管理师】【信息项目十大管理】第二天:项目立项管理
  14. 网页分享至Facebook,Twitter,LinkedIn,WhatsApp,邮箱总结
  15. 1次阿里面试经验,4天走完全部流程,看这里对你有帮助!
  16. 小企业会计准则 ——主要账务处理和财务报表(1)
  17. php 获取上周日期_php获取本周以及上周 开始 和结束的日期
  18. 【Kaldi例子】Librispeech数据整理
  19. 【小程序测试】小程序介绍,项目简介
  20. window global

热门文章

  1. python数据分析 制图_Python与开源GIS:数据处理、空间分析与地图制图
  2. 群之脉PHP面试,面试问Redis集群,被虐的不行了......
  3. acid事务 mysql_MySQL 事务ACID特性
  4. 转:js中arguments详解
  5. 微服务容错限流Hystrix入门
  6. 动态规划——Palindrome Partitioning II
  7. 进入第一个Android应用界面
  8. iOS 网络与多线程--3.异步Get方式的网络请求(非阻塞)
  9. LeetCode - Duplicate Emails
  10. [原创]jQuery的this和$(this)