最近需求有关于通知的,折腾了许久,现在集中学习一下。主要针对 iOS10 的新特性。
基本原理
本地推送(Local Notification)
- App 本地创建通知,加入系统的 Schedule 中。
- 如果达成触发条件则推送相应消息内容。
远程推送(Remote Notification)
- 设备向 APNS(Apple Push Notification Service)发送请求,注册自己。APNS 返回一个
deviceToken
,设备再将发送给 PUSH 服务器。 - 服务器将要发送的消息,目标设备的
deviceToken
打包,发给 APNS。 - APNS 在自身的已注册 Push 服务的 iPhone 列表中,查找有相应标识的iPhone,并把消息发到 iPhone。
- iPhone 把发来的消息传递给相应的应用程序, 并且按照设定弹出 Push 通知。
基本设置
配置
关于推送证书的准备就不说了。除了推送证书,还需要打开 Push Notification 开关进行授权。打开开关后,会自动生成一个 xxx.entitlements 文件,如下图所示:
这里的 value 默认是 development
,不需要我们手动改,Xcode 会在我们发布的时候自动改成 producetion
的。
方法
头文件
导入 #import <UserNotifications/UserNotifications.h>
且要遵守<UNUserNotificationCenterDelegate>
的协议,在 Appdelegate.m中。这里需要注意,我们最好写成这种形式(防止低版本找不到头文件出现问题):
1 |
获取权限与注册
在 Appdelegate 中先向用户获取权限与注册:
1 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { |
其中定义的宏:
1 |
上面方法兼顾了 iOS8 以上的各个系统。调用了上面的方法后,真机就会在屏幕上显示获取通知权限的对话框。
通过 getNotificationSettingsWithCompletionHandler:
这个新方法可以获取用户的设定信息,打印信息如下:
1 | [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { |
- UNUserNotificationCenter 是一个单例的对象,可以在代码的任意位置获取该对象的实例,请求通知权限。
- DeviceToken可能会有更改,因此需要在程序的每次启动都去注册和设置。
获取 deviceToken
上面的方法向苹果注册后,苹果返回 deviceToken
:
1 |
|
接收通知
通知的类型
在 iOS10 中,本地通知和远程通知合二为一。区分本地通知跟远程通知的类是UNPushNotificationTrigger.h
类中,UNPushNotificationTrigger
的类型是新增加的,通过它,我们可以得到一些通知的触发条件 ,解释如下:
UNPushNotificationTrigger
(远程通知) 远程推送的通知类型UNTimeIntervalNotificationTrigger
(本地通知) 一定时间之后,重复或者不重复推送通知。我们可以设置timeInterval(时间间隔)和repeats(是否重复)。UNCalendarNotificationTrigger
(本地通知) 一定日期之后,重复或者不重复推送通知 例如,你每天8点推送一个通知,只要dateComponents为8,如果你想每天8点都推送这个通知,只要repeats为YES就可以了。UNLocationNotificationTrigger
(本地通知)地理位置的一种通知,当用户进入或离开一个地理区域来通知。
iOS10
对于 iOS10,且实现了 UNUserNotificationCenterDelegate
协议,将通过下面两个方法接收和处理通知:
当App处于前台:
1 | - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{ |
这里就可以通过上面列举的类型判断推送的类型了。最后一个执行的 completionHandler(...)
方法可以在前台下也显示横幅。
App 横幅的点击事件:
1 | - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler{ |
这个方法的名字是 didReceiveNotificationResponse
,是把本地推送和远端推送整合在一起了。下面👇的方法就是专门针对远端推送的。
iOS10 前
app 在前台,不会有通知条幅,但是还是能走到下面的方法。进程杀死,点击条幅启动后也能走到下面的方法。
1 | - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { |
如果iOS10,且实现了上面的代理方法,则不会走下面的这个方法。
无论进程是否被杀死,都能走到上面的方法中。
添加推送
本地推送
本地推送的基本流程是:
- 创建一个触发器(trigger)
- 创建推送的内容(UNMutableNotificationContent)
- 创建推送请求(UNNotificationRequest)
- 推送请求添加到推送管理中心(UNUserNotificationCenter)中
创建触发器(trigger)
新功能trigger可以在特定条件触发,有三类: UNTimeIntervalNotificationTrigger
、UNCalendarNotificationTrigger
、UNLocationNotificationTrigger
。
定时推送:
1 | //timeInterval:单位为秒(s) repeats:是否循环提醒 |
定期推送:
1 | //在每周一的14点3分提醒 |
地点推送:
地区信息使用CLRegion的子类CLCircularRegion,可以配置region属性 notifyOnEntry和notifyOnExit,是在进入地区、从地区出来或者两者都要的时候进行通知:
1 | //首先得导入#import <CoreLocation/CoreLocation.h>,不然会regin创建有问题。 |
创建推送的内容(UNMutableNotificationContent)
属性有 title、subtitle、body、badge、sound、lauchImageName、userInfo、attachments、categoryIdentifier、threadIdentifier。
1 | // 创建通知内容 UNMutableNotificationContent, 注意不是 UNNotificationContent ,此对象为不可变对象。 |
categoryIdentifier
后面会用到,就是下面 Notification Actions 里面的 identifier.
创建推送请求(UNNotificationRequest)
1 | // 创建通知标示 |
这里的 identifier
是用来标识本地推送的,用在对本地推送的修改删除,一般用处不是太大。
添加到推送管理中心(UNUserNotificationCenter)中
1 | UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter]; |
远端推送
远端推送需要服务器按照苹果的格式推送 aps 内容:
1 | { |
Notification Actions
这是 iOS10 推出的通知功能拓展,可加可不加,用来给推送添加一些按钮。
创建Action
button型:
1 | UNNotificationAction *lookAction = [UNNotificationAction actionWithIdentifier:@"action.join" title:@"接收邀请" options:UNNotificationActionOptionAuthenticationRequired]; |
其中 UNNotificationActionOptions
是一个枚举类型,用来标识 Action 触发的行为方式分别为:
UNNotificationActionOptionAuthenticationRequired = (1 << 0)
,需要解锁显示,点击不会进appUNNotificationActionOptionDestructive = (1 << 1)
,红色文字。点击不会进appUNNotificationActionOptionForeground = (1 << 2)
,黑色文字,点击会进app
输入框型:
1 | UNTextInputNotificationAction *inputAction = [UNTextInputNotificationAction actionWithIdentifier:@"action.input" title:@"输入" options:UNNotificationActionOptionForeground textInputButtonTitle:@"发送" textInputPlaceholder:@"tell me loudly"] |
输入框型多了两个参数,buttonTitle
输入框右边的按钮标题,placeholder
输入框占位符。
创建category
button型
1 | UNNotificationCategory *notificationCategory = [UNNotificationCategory categoryWithIdentifier:@"Dely_locationCategory" actions:@[lookAction, joinAction, cancelAction] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction]; |
其中:
identifier
: 标识符是这个 category 的唯一标识,用来区分多个 category,这个 id 不管是 Local Notification,还是 remote Notification,一定要有并且要保持一致actions
: 创建的 action 操作数组intentIdentifiers
: 意图标识符 可在<Intents/INIntentIdentifiers.h>
中查看,主要是针对电话、carplay 等开放的 APIoptions
: 一般都是UNNotificationCategoryOptionCustomDismissAction
就行
不同通知,action 的方式也会不同。所以这个
identifier
是用来标识该用哪种方式处理的。对于 remote notification,需要在发给 APNS 的消息中添加category
字段,值与identifier
相同。对于local notification
,需要设置UNMutableNotificationContent
实例的categoryIdentifier
属性。
输入框型
1 | UNNotificationCategory *notificationCategory = [UNNotificationCategory categoryWithIdentifier:@"Dely_locationCategory" actions:@[inputAction] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction]; |
将 category 添加到通知中心
1 | // 将 category 添加到通知中心 |
事件的操作
上面代码并没有添加具体的操作方式,具体的操作都在前面说过的 didReceiveNotificationResponse
方法中进行:
1 | //App通知的点击事件 |
通过 response
拿到前面设置的 identifier
,来按照需求,定制操作。
Media Attachments
这个东西是给推送添加多媒体效果,比如图片,视频等。暂时用到的不多,用到再看。