全部产品
存储与CDN 数据库 安全 应用服务 数加·人工智能 数加·大数据基础服务 互联网中间件 视频服务 开发者工具 解决方案 物联网
移动推送

iOS10通知适配

更新时间:2017-09-27 10:28:33

1. iOS 10通知简介

iOS 10系统对推送通知做了较大增强,远程推送通知相关主要体现在以下几点:

  • 统一通知相关的API和Framework;
  • 通知注册和回调接口修改;
  • 通知内容更丰富,支持富媒体(图片、音频、视频等)推送;
  • 通知详情自定义UI;

总的来说,iOS 10提供了更简洁易用的通知相关接口,提高了处理通知的自由度。

阿里云移动推送对iOS 10通知相关feature配置,通过OpenAPI高级推送接口配置,请参考OpenAPI推送高级接口

阅读本文档时,可参照iOS推送Demo中iOS 10通知相关的代码进行学习和试用。

2. Framework依赖

  • UserNotifications.framework,iOS 10通知相关类和接口都包含在内;
  • 引用如下:
  1. #import <UserNotifications/UserNotifications.h>

3. 通知字段

  • 原本iOS通知仅支持设置通知内容,可通过OpenAPI的Summary字段设置,参考OpenAPI推送高级接口字段老版本服务端推送通知payload字段如下:
  1. {
  2. "aps": {
  3. "alert": {
  4. "your notification body",
  5. },
  6. "badge": 1,
  7. "sound": "default",
  8. },
  9. "key1":"value1",
  10. "key2":"value2"
  11. }
  • iOS 10通知支持设置标题(title)副标题(subtitle)内容(body)通知扩展字段(mutable-content)通知类别(category),服务端配置参考上述的OpenAPI推送高级接口,当前服务端推送通知payload字段参考如下:
  1. {
  2. "aps": {
  3. "alert": {
  4. "title": "title",
  5. "subtitle": "subtitle",
  6. "body": "body"
  7. },
  8. "badge": 1,
  9. "sound": "default",
  10. "category": "test_category",
  11. "mutable-content": 1
  12. },
  13. "key1":"value1",
  14. "key2":"value2"
  15. }
  • 【注意】使用OpenAPI推送时,若没有进行iOS 10通知相关配置,通知payload保持老版本不变,已经保证对老版本payload兼容性;若进行iOS 10通知相关配置,请确保客户端业务逻辑对payload相关字段处理的兼容性。

  • iOS 10设备收到通知效果如下,其中标题为”aliyun”,副标题为”push”,内容为”haha”。title设置后,iOS 10+系统显示title如下;【iOS 8.2 <= iOS系统 < iOS 10】,通知应用名称会显示该标题。

ios10-fit

4. 通知中心

4.1 简介

  • 基于UNUserNotificationCenter对象进行通知的调度和管理通知相关的行为,具体如下:
    • 为通知的提醒、声音和角标请求授权;
    • 声明通知类别和可执行动作;
    • 管理通知的弹出;
    • 管理通知在设备通知中心的展示;
    • 获取App通知相关的配置。
  • 通知中心对象获取方式如下:
    1. UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];

4.2 请求授权并向APNs注册

  • App使用推送功能前,需要向用户请求推送功能授权,如下所示;
  • 第一次调用requestAuthorizationWithOptions请求授权时,App会弹出如下图所示的授权框,注意App卸载重装前该授权框仅弹出一次,若用户点击“不允许”,需要引导用户到“设置”中打开,推送功能才能正常使用。

iOS10-notice-auth

  • requestAuthorizationWithOptions回调中可捕获用户是否点击授权,在成功授权回调中调用registerForRemoteNotifications,向APNs注册获取设备的deviceToken,App再次启动时虽然不会弹出授权框,但推送授权请求可获取App推送配置,可触发成功/失败授权回调。
  • APNs注册成功/失败回调保持不变,在成功回调中调用阿里云推送SDK接口,将deviceToken上报到阿里云推送服务器。
  • 主动调用getNotificationSettingsWithCompletionHandler接口,回调中可获取App推送授权状态。
  1. UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  2. if (systemVersionNum >= 10.0) {
  3. center = [UNUserNotificationCenter currentNotificationCenter];
  4. [center requestAuthorizationWithOptions:UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) {
  5. if (granted) {
  6. // granted
  7. NSLog(@"User authored notification.");
  8. dispatch_async(dispatch_get_main_queue(), ^{
  9. [application registerForRemoteNotifications];
  10. };
  11. } else {
  12. // not granted
  13. NSLog(@"User denied notification.");
  14. }
  15. }];
  16. /*
  17. * APNs注册成功回调,将返回的deviceToken上传到CloudPush服务器
  18. */
  19. - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  20. NSLog(@"Upload deviceToken to CloudPush server.");
  21. [CloudPushSDK registerDevice:deviceToken withCallback:^(CloudPushCallbackResult *res) {
  22. if (res.success) {
  23. NSLog(@"Register deviceToken success, deviceToken: %@", [CloudPushSDK getApnsDeviceToken]);
  24. } else {
  25. NSLog(@"Register deviceToken failed, error: %@", res.error);
  26. }
  27. }];
  28. }
  29. /*
  30. * APNs注册失败回调
  31. */
  32. - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
  33. NSLog(@"Get deviceToken failed, error: %@", error);
  34. }
  35. // 主动获取设备通知是否授权(iOS 10+)
  36. - (void)getNotificationSettingStatus {
  37. [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
  38. if (settings.authorizationStatus == UNAuthorizationStatusAuthorized) {
  39. NSLog(@"User authed.");
  40. } else {
  41. NSLog(@"User denied.");
  42. }
  43. }];
  44. }

4.3 Action和Category

  • iOS 8以上支持,此处仅讲述iOS 10系统的实现方式。
  • 通知支持设置Action点击动作,即在通知上添加按钮,点击按钮可触发回调以此做出不同的逻辑处理;
  • 通知支持Category分类,可将ActionCategory进行关联,Category和第6节(通知详情自定义UI)相关。
  • 下面代码自定义id为action1action2的通知动作,创建id为test_category的通知类别后,将两个Action关联到该category,最后注册category到通知中心。
  • 使用OpenAPI推送通知时,调用setiOSNotificationCategory()接口,可指定通知的类别;创建的test_category类别的通知弹出时如下图所示,test1test2按钮分别对应id为action1action2的通知Action;
  • 【注意】Category注册到通知中心需要在推送前完成。

ios10-category

  1. /**
  2. * 创建并注册通知category(iOS 10+)
  3. */
  4. - (void)createCustomNotificationCategory {
  5. // 自定义`action1`和`action2`
  6. UNNotificationAction *action1 = [UNNotificationAction actionWithIdentifier:@"action1" title:@"test1" options: UNNotificationActionOptionNone];
  7. UNNotificationAction *action2 = [UNNotificationAction actionWithIdentifier:@"action2" title:@"test2" options: UNNotificationActionOptionNone];
  8. // 创建id为`test_category`的category,并注册两个action到category
  9. // UNNotificationCategoryOptionCustomDismissAction表明可以触发通知的dismiss回调
  10. UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:@"test_category" actions:@[action1, action2] intentIdentifiers:@[] options:
  11. UNNotificationCategoryOptionCustomDismissAction];
  12. // 注册category到通知中心
  13. [center setNotificationCategories:[NSSet setWithObjects:category, nil]];
  14. }

4.4 通知回调

  • UNUserNotificationCenterDelegate协议定义了通知相关的回调;

4.4.1 设置代理

  • 若要处理通知相关回调,需要实现并指定通知中心对象UNUserNotificationCenter的代理UNUserNotificationCenterDelegate,一般是在AppDelegate中实现,如下所示:
  1. @interface AppDelegate () <UNUserNotificationCenterDelegate>
  2. @end
  3. @implementation AppDelegate
  4. ...
  5. UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  6. center.delegate = self;
  7. ...
  8. @end

4.4.2 回调1:App在前台收到通知

  • 当App处于前台,收到通知会触发userNotificationCenter:willPresentNotification:withCompletionHandler:回调;
  • 在回调中可以处理通知相关的字段信息,回调处理结束前需要调用completionHandler(UNNotificationPresentationOptions)UNNotificationPresentationOptions的参数含义如下:
    • UNNotificationPresentationOptionNone,通知不提醒;
    • UNNotificationPresentationOptionSound,通知声音提醒;
    • UNNotificationPresentationOptionAlert,通知内容提醒;
    • UNNotificationPresentationOptionBadge,通知角标提醒。
  • 基于此,App在前台时也可以将通知弹出。
  1. /**
  2. * 处理iOS 10通知(iOS 10+)
  3. */
  4. - (void)handleiOS10Notification:(UNNotification *)notification {
  5. UNNotificationRequest *request = notification.request;
  6. UNNotificationContent *content = request.content;
  7. NSDictionary *userInfo = content.userInfo;
  8. // 通知时间
  9. NSDate *noticeDate = notification.date;
  10. // 标题
  11. NSString *title = content.title;
  12. // 副标题
  13. NSString *subtitle = content.subtitle;
  14. // 内容
  15. NSString *body = content.body;
  16. // 角标
  17. int badge = [content.badge intValue];
  18. // 取得通知自定义字段内容,例:获取key为"Extras"的内容
  19. NSString *extras = [userInfo valueForKey:@"Extras"];
  20. // 通知打开回执上报
  21. [CloudPushSDK handleReceiveRemoteNotification:userInfo];
  22. NSLog(@"Notification, date: %@, title: %@, subtitle: %@, body: %@, badge: %d, extras: %@.", noticeDate, title, subtitle, body, badge, extras);
  23. }
  24. /**
  25. * App处于前台时收到通知(iOS 10+)
  26. */
  27. - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
  28. NSLog(@"Receive a notification in foregound.");
  29. // 处理iOS 10通知相关字段信息
  30. [self handleiOS10Notification:notification];
  31. // 通知不弹出
  32. //completionHandler(UNNotificationPresentationOptionNone);
  33. // 通知弹出,且带有声音、内容和角标(App处于前台时不建议弹出通知)
  34. completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
  35. }

4.4.3 回调2:点击/清除通知

  • 点击/清除通知时, 可在userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:回调里捕获到这些动作,根据UNNotificationResponse.actionIdentifier可对这些动作进行区分:
    • 点击通知打开App,对应UNNotificationDefaultActionIdentifier
    • 左滑删除通知,对应UNNotificationDismissActionIdentifier,注册Category时,需传入UNNotificationCategoryOptionCustomDismissAction才可以捕获到该动作,具体见4.3节的Category创建和注册;
    • 点击自定义Action,如点击4.3节创建的id为action1action2的Action,自定义Action点击动作的捕获的好处在于,即使不进入App同样可完成某些逻辑处理。
  • 【注意】两个通知回调是不冲突的,当App处于前台时,收到通知先触发userNotificationCenter:willPresentNotification:withCompletionHandler:回调;之后若有点击通知动作,再触发userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:回调。
  1. /**
  2. * 触发通知动作时回调,比如点击、删除通知和点击自定义action(iOS 10+)
  3. */
  4. - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
  5. NSString *userAction = response.actionIdentifier;
  6. // 点击通知打开
  7. if ([userAction isEqualToString:UNNotificationDefaultActionIdentifier]) {
  8. NSLog(@"User opened the notification.");
  9. // 处理iOS 10通知,并上报通知打开回执
  10. [self handleiOS10Notification:response.notification];
  11. }
  12. // 通知dismiss,category创建时传入UNNotificationCategoryOptionCustomDismissAction才可以触发
  13. if ([userAction isEqualToString:UNNotificationDismissActionIdentifier]) {
  14. NSLog(@"User dismissed the notification.");
  15. }
  16. NSString *customAction1 = @"action1";
  17. NSString *customAction2 = @"action2";
  18. // 点击用户自定义Action1
  19. if ([userAction isEqualToString:customAction1]) {
  20. NSLog(@"User custom action1.");
  21. }
  22. // 点击用户自定义Action2
  23. if ([userAction isEqualToString:customAction2]) {
  24. NSLog(@"User custom action2.");
  25. }
  26. completionHandler();
  27. }

5. 富媒体推送

  • iOS 10添加了通知相关的扩展Notification Service Extension,使得通知弹出前可以对通知内容进行修改。
  • iOS远程推送过程如下图所示,APNs推送的通知直接在设备上弹出;ios-remote-notice
  • 添加Notification Service Extension后,如下图所示,APNs推送的通知在弹出前,可先到达Extension进行处理,【注意】OpenAPI需要调用setiOSMutableContent(true)接口,这样Extension才可生效。ios-service-extension
  • Service Extension后台预处理阶段,可从远程服务器下载或从本地获取富媒体(图片、音频、视频)资源,将其作为attachment添加到通知中,富媒体资源类型和大小限制如下:ios-media-limit

  • Notification Service Extension添加步骤:

    • Xcode -> File -> New -> Target,选择Notification Service Extension,如下图所示:ios-service-extension-target
    • 输入Target名,创建完成后在目录下Xcode会自动生成NotificationService的模板,在didReceiveNotificationRequest回调方法中,处理通知弹出前的动作。
  • 可参考iOS Demo Notification Service Extension的实现携带图片的推送通知,从OpenAPI设定的自定义参数attachment字段中获取图片Url,或者从本地获取图片资源,效果如下图所示。
  • 【注意】从远程服务器获取富媒体资源时,同样需遵循App Transport Security (ATS)的原则,若需要请求HTTP资源请参考ATS配置,为Service Extension Target进行配置;建议限制为请求HTTPS资源。

ios-pic-notice

6. 通知详情自定义UI

  • 除了Notification Service Extension,另一个通知相关的Extension为内容扩展Content Extension,可用于自定义通知详情UI,如修改样式、颜色等。
  • iOS 10收到通知后,支持下拉通知(经测试iPhone 5c不支持,建议使用iPhone 6以上手机测试) or 3D touch展开通知详情,携带图片的通知详情样式默认如下图所示,内容扩展可针对通知详情进行定制。

ios-content-ui

  • 内容扩展添加步骤:
    • Xcode -> File -> New -> Target,选择Notification Content,如下图所示:ios10-notice-content
    • 输入Target名,Xcode自动生成NotificationViewController头文件和源文件,MainInterface.storyboardInfo.plist,其中NotificationViewControllerMainInterface.storyboard一起定义了通知详情的UI。
    • Info.plist中自动生成NSExtension相关KV配置,具体含义如下所示:
      • NSExtensionAttributes
        • UNNotificationExtensionCategory,指定自定义通知详情UI适用于哪些category,可为String or Dictionary;(必需)
        • UNNotificationExtensionInitialContentSizeRatio,通知视图长宽比例;(必需)
        • UNNotificationExtensionDefaultContentHidden,原本通知内容是否隐藏,若不指定,默认为NO;(可选)
      • NSExtensionMainStoryboard,storyboard文件名,默认填充为MainInterface;(必需)
      • NSExtensionPointIdentifier,默认填充为com.apple.usernotifications.content-extension;(必需)
  • OpenAPI推送时,必需通过setiOSNotificationCategory接口指定通知category,只有指定的category在Info.plist的UNNotificationExtensionCategory设置,才能保证通知详情自定义UI生效。
  • 参考iOS Demo Notification Content Extension的实现方式,进行通知详情自定义UI的设置,如下图所示,绿色的aliyun-body为自定义的展示UI,字段内容通过拷贝通知内容得来。

ios-custom-content

本文导读目录