Fork me on GitHub

iOS10自定义推送UI和推送内容(上)

前言

iOS10这个系统已经出来一年多了,那时候系统刚发布的时候也做过iOS10推送的适配,但是那时候推送是集成第三方的,所以对新的系统的推送的升级不是很了解,UserNotifications.frameworks和UserNotificationUI.frameworks是在iOS10上发布的,可以认为是通知大改版,用于在新系统上替换久的通知框架,苹果给了我们一个能够自定义推送的一个机会,但是因为那是时候的工作没有涉及到这一块,只是对iOS10上的新通知框架有个初步的了解,并没有深入学习这两个新的推送框架,因为最近产品上有这样的需求,需要自定义推送的UI和统计推送的送达率等,所以对这两个框架进行了深入的学习,并且写篇文章总结一下,本篇文章最要是围绕着Notification Service Extension和Notification Content Extension来展开,有关iOS10系统的通知特性请看文章下面推荐的文章。

知识准备

在深入学习之前,如果大家没有看过iOS10推送相关的内容,这里推荐一篇文章供大家学习和了解一下iOS10推送相关的一些知识,本篇文章涉及的内容跟下面的文章会有所关联,当然会深入一些,学习嘛~需要循序渐进

自定义推送内容

在iOS 10以上版本的系统,苹果推送了Notification Service Extension(通知内容扩展)给我们自定义推送的内容

1、Notification Service Extension简介

Notification Service Extension是在iOS10推出后的一个新的通知特性,原理如下图(图片来源网络):

Alt text

从图片我们可以看出,当手机接收到APNs推送给用户的消息后,中间添加了Service Extension这一个处理的步骤(当然也可以啥都不处理),通过这个扩展我们可以把我们要自定义的内容的处理放在这个步骤里面做,然后再呈现给用户,使得推送的内容更加丰富化,当然这里是有时间限制的(后面会详细讲)

2、Notification Service Extension能做些什么事情

  • 统计推送送达率
  • 自定义内容,推送内容丰富化
  • 安全,可以防止推送内容被拦截和篡改

3、Notification Service Extension构建和使用

如果有使用过Extension的同学都知道,其实Extension在工程里面就是一个target,那么Noification Service Extension也不例外也是一个target:

Alt text

接着选择Notification Service Extension,如下图:

Alt text

完成后就会在主工程上看到对应的Notification Service Extension的target:

Alt text

在Notification Service Extension里面会看到有两个函数:

Alt text

通知的内容自定义在这个函数处理,可以处理附件的下载、推送内容修改、推送送达率的统计等:

1
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void)

这个函数是当你接收到一条远程通知后,系统会调用override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void)这个函数给你处理通知的内容,当然系统也不会给很长的时间,长时间等待会造成体验不好什么的,系统只给你30秒的时间处理,如果处理不完会调用override func serviceExtensionTimeWillExpire()这个函数,如果你还不做出相应的操作,那么将显示最开始的通知的内容。

1
override func serviceExtensionTimeWillExpire()

例如推送统计送达率,推送送达率其实就是手机接收到通知后,系统调起了Notification Service Extension,那么这时候在接收到通知的回调里面,发送一个请求告诉服务端手机客户端已经接收到这条通知,以达到推送送达率统计的目的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

//TODO 推送统计通达率
let url: URL = URL.init(string: "your https address")!
let urlRequest: URLRequest = URLRequest.init(url: url)
let urlSession: URLSession = URLSession.init(configuration: URLSessionConfiguration.default)
let task: URLSessionDataTask = urlSession.dataTask(with: urlRequest) { (data, res, error) in

//统计完了回调通知
if let bestAttemptContent = self.bestAttemptContent {
bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
contentHandler(bestAttemptContent)
}
}

task.resume()
}

例如内容自定义:

1
2
3
4
5
6
7
8
9
10
11
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

//TODO: 修改推送的标题和内容等
if let bestAttemptContent = self.bestAttemptContent {
bestAttemptContent.title = "这是我自定义推送的标题!!!🐶🐶"
bestAttemptContent.body = "这是我自定义推送的内容!!!🍎🍐"
contentHandler(bestAttemptContent)
}
}

4、Notification Service Extension配置

前面说到Extension是一个target,那么就会涉及到打包相关的工作,这里需要配置对应的bundle identifier和打包的描述文件:

Alt text

推送环境的配置和勾选,需要开启push Notifications配置:

Alt text

假如你需要在网络上下载一些资源或者是一些数据请求,那么需要配置info.plist文件允许网络访问权限(如果不配置会导致程序奔溃):

1
2
3
4
5
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

关于调试

对于Notification Service Extension的调试,我在开发的过程中遇到了不少的问题,也花了不少的时间去查资料等,国内这一块的资料比较少而且也不全面,所以这里补充一下我遇到的问题和才到的坑

  • 程序编译了,当接收到通知的时候代码没有起作用,其实是调试的方式不对,首先是要调试主工程,然后再调试Notifcation Service Extension,再回到主工程调试Notification Service Extension的代码就会执行,你修改的内容就会在通知上看到

  • 关于debug断点,是要在主工程的调试的时候去监听Notification Service Extension 才能断点,不能直接在Notification Service Extension调试的时候去断点调试,如果在这里调试可以在macOS的控制台App中看到Notification Service Extension启动后被系统kill了

  • 关于前面连个步骤都不能起效果,是因为你的APNs推送的数据个数不对,需要加上mutable-content字段

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"aps": {
"alert": {
"title": "推送测试",
"body": "推送内容",
"type": 20
},
"badge": 6,
"sound": "default",
"mutable-content": 1,
"category": "customUI"
}
}

推送调试工具

这里安利大家两个能够不需要服务器配置也能够测试的小工具

  • SmartPush
  • Easy APNs Provider,App Store可以下载