iOS 10 的 UserNotifications 框架解析及使用

846 阅读8分钟

写在最前

在iOS 10中,之前有关Remote Notification和Local Notification的的杂乱的API被进行了重构,并在iOS 8 以及iOS 9的基础之上进行了功能新的扩展,推出了全新并且独立的 UserNotifications.framework 来集中管理相关通知的各种API。

相对于开发者来说使用起来更加简单易用,相对于运营环节来说,因为新的框架有了Attachment以及自定义UI等新的功能,所以有更加灵活的方法来处理相关的通知。

接口分析


UserNotifications.h

以上为新的UserNotifications.framework 为我们带来的各项接口。

1.

类中只有一个方法

// Use -[NSString localizedUserNotificationStringForKey:arguments:] to provide a string that will be localized at the time that the notification is presented.
+ (NSString *)localizedUserNotificationStringForKey:(NSString *)key arguments:(nullable NSArray *)arguments __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0) __TVOS_PROHIBITED;

主要作用是用来提供将在该通知被呈现在时间进行本地化字符串。

2.

只有一个属性UNErrorDomain以及一个枚举UNErrorCode,顾名思义是作为错误属性的值。

3.

拥有两个属性

// The date displayed on the notification.
@property (nonatomic, readonly, copy) NSDate *date;
// The notification request that caused the notification to be delivered.
@property (nonatomic, readonly, copy) UNNotificationRequest *request;

一般作为返回的消息实例。

4.

从名字就可以看出,类的作用就是定义了收到通知后的一些Action操作,有identifier,title,options三个属性,其中options是一个枚举类型

typedef NS_OPTIONS(NSUInteger, UNNotificationActionOptions) {

    // Whether this action should require unlocking before being performed.
    //button为红色,点需要解锁屏幕显示
    UNNotificationActionOptionAuthenticationRequired = (1 << 0),
    // Whether this action should be indicated as destructive
    //button为黑色,点击不会进入App
    UNNotificationActionOptionDestructive = (1 << 1),
    // Whether this action should cause the application to launch in the foreground.
    //button为黑色,点击进入App
    UNNotificationActionOptionForeground = (1 << 2),
} __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0) __TVOS_PROHIBITED;

拥有有一个子类UNTextInputNotificationAction,所谓类似回复微信和iMessage时候使用。

5.

通知的附件属性类,里面三个主要属性

// The identifier of this attachment
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSString *identifier;

// The URL to the attachment's data. If you have obtained this attachment from UNUserNotificationCenter then the URL will be security-scoped.
//附件相关的地址,可为本地地址
@property (nonatomic, readonly, copy) NSURL *URL;

// The UTI of the attachment.
@property (nonatomic, readonly, copy) NSString *type;

另有4个常量

// Key to manually provide a type hint for the attachment. If not set the type hint will be guessed from the attachment's file extension. Value must be an NSString.
//手动提供附件类型提示的键。如果没有设置类型提示,将会从附件文件扩展名中猜出类型。值必须是一个NSString格式。
extern NSString * const UNNotificationAttachmentOptionsTypeHintKey __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);

// Key to specify if the thumbnail for this attachment is hidden. Defaults to NO. Value must be a boolean NSNumber.
// 注明此附件缩略图是隐藏的键。默认值为NO。值必须是一个NSNumber类型的布尔值。
extern NSString * const UNNotificationAttachmentOptionsThumbnailHiddenKey __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);

// Key to specify a normalized clipping rectangle to use for the attachment thumbnail. Value must be a CGRect encoded using CGRectCreateDictionaryRepresentation.
//指定一个标准化的剪辑矩形用于附件缩略图的键。值必须是使用DictionaryRepresentation编码的一个CGRect值。
extern NSString * const UNNotificationAttachmentOptionsThumbnailClippingRectKey __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);

// Key to specify the animated image frame number or the movie time to use as the thumbnail.
// An animated image frame number must be an NSNumber. A movie time must either be an NSNumber with the time in seconds or a CMTime encoded using CMTimeCopyAsDictionary.
//指定动画形象帧数或用缩略图的电影时间的键。
//动画图像帧数必须是一个NSNumber对象。电影的时间必须是一个NSNumber对象的以秒为单位的时间或使用CMTimeCopyAsDictionary编码的CMTime值。
extern NSString * const UNNotificationAttachmentOptionsThumbnailTimeKey __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);
6.

用于储存Action的类

// The unique identifier for this category. The UNNotificationCategory's actions will be displayed on notifications when the UNNotificationCategory's identifier matches the UNNotificationRequest's categoryIdentifier.
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSString *identifier;

// The UNNotificationActions in the order they will be displayed.
//储存我们设置的Actions的数组
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSArray *actions;

// The intents supported support for notifications of this category. See  for possible values.
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSArray *intentIdentifiers;

@property (NS_NONATOMIC_IOSONLY, readonly) UNNotificationCategoryOptions options;

options是一个权限的枚举值,两者可以一起使用

typedef NS_OPTIONS(NSUInteger, UNNotificationCategoryOptions) {
    UNNotificationCategoryOptionNone = (0),

    // Whether dismiss action should be sent to the UNUserNotificationCenter delegate
    UNNotificationCategoryOptionCustomDismissAction = (1 << 0),

    // Whether notifications of this category should be allowed in CarPlay
    UNNotificationCategoryOptionAllowInCarPlay = (2 << 0),

} __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0) __TVOS_PROHIBITED;
7.

可以简单看做是储存所有通知信息的Model类,里面有attachments、sound、title以及userInfo等信息。

拥有一个子类UNMutableNotificationContent,和父类的区别从名字就可以看出是可以修改的,个属性中也比父类少了一个readonly属性。

8.

有三个属性,分别是标示符、消息Model以及NotificationTrigger。

// The unique identifier for this notification request. It can be used to replace or remove a pending notification request or a delivered notification.
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSString *identifier;

// The content that will be shown on the notification.
@property (NS_NONATOMIC_IOSONLY, readonly, copy) UNNotificationContent *content;

// The trigger that will or did cause the notification to be delivered. No trigger means deliver now.
@property (NS_NONATOMIC_IOSONLY, readonly, copy, nullable) UNNotificationTrigger *trigger;
9.

通过名字就可以看出来,与request对应作为相应通知的Action使用。
用户可以拿到整个通知的内容,以及操作所对应的标示符。

// The notification to which the user responded.
@property (NS_NONATOMIC_IOSONLY, readonly, copy) UNNotification *notification;

// The action identifier that the user chose:
// * UNNotificationDismissActionIdentifier if the user dismissed the notification
// * UNNotificationDefaultActionIdentifier if the user opened the application from the notification
// * the identifier for a registered UNNotificationAction for other actions
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSString *actionIdentifier;

子类UNTextInputNotificationResponse还包含userText,可以取到用户输入的内容。

10.

主要是通知的一些属性设置,对应属性,有三个枚举值

typedef NS_ENUM(NSInteger, UNAuthorizationStatus) {
     // The user has not yet made a choice regarding whether the application may post user notifications.
    UNAuthorizationStatusNotDetermined = 0,

    // The application is not authorized to post user notifications.
    UNAuthorizationStatusDenied,

    // The application is authorized to post user notifications.
    UNAuthorizationStatusAuthorized
} __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);

typedef NS_ENUM(NSInteger, UNNotificationSetting) {
    // The application does not support this notification type
    UNNotificationSettingNotSupported  = 0,

    // The notification setting is turned off.
     UNNotificationSettingDisabled,

    // The notification setting is turned on.
    UNNotificationSettingEnabled,
} __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);

typedef NS_ENUM(NSInteger, UNAlertStyle) {
    UNAlertStyleNone = 0,
    UNAlertStyleBanner,
    UNAlertStyleAlert,
} __IOS_AVAILABLE(10.0) __TVOS_PROHIBITED __WATCHOS_PROHIBITED;
11.

两个方法,一个是设置为默认提示音,一个是设置为自定义提示音。
可以直接使用声音的name,而不是文件路径。

// The default sound used for notifications.
+ (instancetype)defaultSound;

// The name of a sound file to be played for the notification. The sound file must be contained in the app’s bundle or in the Library/Sounds folder of the app's data container. If files exist in both locations then the file in ~/Library/Sounds will be preferred.
+ (instancetype)soundNamed:(NSString *)name __WATCHOS_PROHIBITED;
12.

NotificationTrigger算是新增的比较重要的功能,四种方式为App的运用提供了更多可选择的方法。其中,日历和地理位置是新增Trigger。

所以,此类下面有四个子类:push通知触发, 时间通知触发,日历通知触发,地区通知触发。与之相对应的,子类拥有自己出发的属性。


UNNotificationTrigger
13.

吐个槽,为毛和USNotificationCenter长得这么想,第一眼看差点蒙了。

最主要的类,通知的注册,激活,编辑,删除等功能都由该类完成。
通过[UNUserNotificationCenter currentNotificationCenter]单例方法进行操作。

14.

里面有两个方法,收到通知的请求后调用, 系统将要销毁时调用。

15.

这个类不在UserNotifications.framework中,主要是用作修改通知展示的UI,使用时需要添加UserNotificationsUI.framework。
下文会做详细介绍。

Notification Extension

从Share Extension等开始,iOS逐渐支持一些利用沙盒等方法的扩展方法。在iOS 10中,与通知有关的有两个:Service ExtensionContent Extension
前者可以让我们有机会在收到远程推送的通知后,展示之前对通知内容进行自定义修改;后者可以用来自定义通知视图的UI样式。


Notification Extensions
iOS 10 中被标为弃用的 API
  • UILocalNotification
  • UIMutableUserNotificationAction
  • UIMutableUserNotificationCategory
  • UIUserNotificationAction
  • UIUserNotificationCategory
  • UIUserNotificationSettings
  • handleActionWithIdentifier:forLocalNotification:
  • handleActionWithIdentifier:forRemoteNotification:
  • didReceiveLocalNotification:withCompletion:
  • didReceiveRemoteNotification:withCompletion:

Demo

Demo地址:github.com/derekhuangx…

因为iOS 10更改了对于获取权限的限制,所以作为适配来说,希望大家在info.plist文件中更改对于权限的获取相关字段。

1、文字通知

文字通知相对简单,只是需要创建UNMutableNotificationContent实体类,创建UNTimeIntervalNotificationTrigger的实体类,然后加入到通知中心的单例中,然后触发即可。

- (IBAction)btnCreateWordNotofication:(id)sender {

    UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc]init];
    content.title = @"NotificationCenter";
    content.subtitle = @"testWord";
    content.body = @"本地通知测试";
    UNTimeIntervalNotificationTrigger * triger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5 repeats:NO];
    NSString * requestIdentifier = @"request";
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:requestIdentifier content:content trigger:triger];
    [[UNUserNotificationCenter currentNotificationCenter]addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {

    }];
}

2、图片通知

与纯文字通知相似,但是需要额外在UNMutableNotificationContent中添加UNNotificationAttachment属性,剩下的基本相似。需要注意的是,content.attachments是一个数组。

- (IBAction)btnCreatePictrueNotofication:(id)sender {

    UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc]init];
    content.title = @"NotificationCenter";
    content.subtitle = @"testWord";
    content.body = @"本地通知测试";

    NSString * imagePath = [[NSBundle mainBundle]pathForResource:@"Notification_Image" ofType:@"png"];
    if (imagePath) {
        UNNotificationAttachment * imageAttachment = [UNNotificationAttachment attachmentWithIdentifier:@"imagePath"

URL:[NSURL fileURLWithPath:imagePath]
options:nil
error:nil];
if (imageAttachment) {
content.attachments = @[imageAttachment];
}
}

    UNTimeIntervalNotificationTrigger * triger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5 repeats:NO];
    NSString * requestIdentifier = @"request";
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:requestIdentifier content:content trigger:triger];
    [[UNUserNotificationCenter currentNotificationCenter]addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {

    }];
}

3、视频通知

视频通知和图片通知的方法是同样的,但是在Demo中,我在视频通知的方法里面添加了3个Action,通过方法,添加到通知中心的单例即可,具体使用方法和作用上文已有介绍。

- (IBAction)btnCreateVideoNotofication:(id)sender {

    UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc]init];
    content.title = @"NotificationCenter";
    content.subtitle = @"testWord";
    content.body = @"本地通知测试";

    NSString * videoPath = [[NSBundle mainBundle]pathForResource:@"Notification_video" ofType:@"mp4"];
    if (videoPath) {
        UNNotificationAttachment * videoAttachment = [UNNotificationAttachment attachmentWithIdentifier:@"videoPath"
                                                                                                    URL:[NSURL fileURLWithPath:videoPath]
                                                                                                options:nil
                                                                                                  error:nil];
        if (videoAttachment) {
            content.attachments = @[videoAttachment];
        }
    }

    NSMutableArray * actionArr = [NSMutableArray array];
    UNNotificationAction * actionA = [UNNotificationAction actionWithIdentifier:@"IdentifierUnlock" title:@"进入应用" options:UNNotificationActionOptionForeground];
    UNNotificationAction * actionB = [UNNotificationAction actionWithIdentifier:@"IdentifierRed" title:@"Another Action" options:UNNotificationActionOptionDestructive];
    UNNotificationAction * actionC = [UNNotificationAction actionWithIdentifier:@"IdentifierBlack" title:@"Third Action" options:UNNotificationActionOptionAuthenticationRequired];

    [actionArr addObjectsFromArray:@[actionA, actionB, actionC]];
    if (actionArr.count > 0) {
        UNNotificationCategory * categoryNotification = [UNNotificationCategory categoryWithIdentifier:@"categoryOperationAction"
                                                                                               actions:actionArr
                                                                                     intentIdentifiers:@[]
                                                                                               options:UNNotificationCategoryOptionCustomDismissAction];
        [[UNUserNotificationCenter currentNotificationCenter]setNotificationCategories:[NSSet setWithObject:categoryNotification]];
        content.categoryIdentifier = @"categoryOperationAction";
    }

    UNTimeIntervalNotificationTrigger * triger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5 repeats:NO];
    NSString * requestIdentifier = @"request";
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:requestIdentifier content:content trigger:triger];
    [[UNUserNotificationCenter currentNotificationCenter]addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {

    }];
}

最后

本文主要介绍了API的使用,简单书写了一个本地触发通知的简单Demo,后面会单研究一下关于远程推送多媒体通知和自定义UI通知。

前有Apple Watch,后面有新的widget和新的UserNotifications.framework,苹果似乎很努力的将我们沉浸在手机中的注意力解脱出来,极力避免我们被纷繁的通知和short action打扰而不得不打开App的可能性。

不知道这算不算Apple对于手机的使用又将有新的定义。

参考