iOS学习笔记iOS - 推送通知

iOS10推送通知整理总结

2017-10-25  本文已影响0人  王技术
这篇文章整理iOS10之后的推送通知(文中的推送通知,如不做特殊说明,默认是iOS10以后的推送通知)

iOS10之前的推送通知,请看这篇
(本文用到的远程推送工具PushMeBaby,也在上篇文章中)


在iOS10上
苹果将原来散落在UIKit中各处的用户通知相关的代码进行重构,剥离
打造了一个全新的通知框架-UserNotifications
因公司业务需求,对这方面做了一些研究
总结此文

1.二话不说,先发一条最简单的通知,然后逐步介绍

从本地通知开始:

//注册通知:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    [center setNotificationCategories:[self createNotificationCategoryActions]];
    // 必须写代理,不然无法监听通知的接收与点击
    center.delegate = self;
    //判断当前注册状态
    [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
        if (settings.authorizationStatus==UNAuthorizationStatusNotDetermined) {
            [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
                if (granted) {
                    [[UIApplication sharedApplication] registerForRemoteNotifications];
                }
            }];
        }
    }];
    return YES;
}
//添加category:
-(NSSet *)createNotificationCategoryActions{
    //注册本地通知用到的Action
    //进入app按钮
    UNNotificationAction * localAction = [UNNotificationAction actionWithIdentifier:@"localAction" title:@"处理本地通知" options:UNNotificationActionOptionForeground];
    ///回复文本按钮
    UNTextInputNotificationAction * localText = [UNTextInputNotificationAction actionWithIdentifier:@"localText" title:@"本地文本" options:UNNotificationActionOptionNone];
    //取消按钮
    UNNotificationAction *localCancel = [UNNotificationAction actionWithIdentifier:@"localCancel" title:@"取消" options:UNNotificationActionOptionDestructive];
    //将这些action带入category
    UNNotificationCategory *localCategory = [UNNotificationCategory categoryWithIdentifier:@"localCategory" actions:@[localAction,localText,localCancel] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];
    return [NSSet setWithObjects:remoteCategory,localCategory,nil];
}
//发送本地通知 :
- (IBAction)pushNotifacation:(UIButton *)sender {
    UNMutableNotificationContent *notificationContent = [[UNMutableNotificationContent alloc]init];
    notificationContent.title = @"iOS10本地通知";
    notificationContent.subtitle = @"扶我起来";
    notificationContent.body = @"我要写代码";
    notificationContent.badge = @1;
    notificationContent.userInfo = @{@"content" : @"我是userInfo"};
    //添加音效
    UNNotificationSound *sound = [UNNotificationSound soundNamed:@"caodi.m4a"];
    notificationContent.sound = sound;
    //添加触发器
    UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:2.0 repeats:NO];
    NSString *identifer = @"identifer";
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifer content:notificationContent trigger:trigger];
    [[UNUserNotificationCenter currentNotificationCenter]addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
        if (!error) {
            NSLog(@"发送成功");
        }
    }];
}
本地通知效果图

接下来介绍代码中的每一步:

iOS10中添加了新接口,可以判断当前推送通知的授权状态:

    [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
          if (settings.authorizationStatus==UNAuthorizationStatusNotDetermined) {
              //判断当没有拿到权限的时候,开始注册推送通知权限
              //注册成功以后,如果需要远程通知,则注册deviceToken
        }
    }];
    UNNotificationSound *sound = [UNNotificationSound soundNamed:@"caodi.m4a"];
    notificationContent.sound = sound;
    NSString *imageFile = [[NSBundle mainBundle]pathForResource:@"sport" ofType:@"png"];
    UNNotificationAttachment *image = [UNNotificationAttachment attachmentWithIdentifier:@"image" URL:[NSURL fileURLWithPath:imageFile] options:nil error:nil];
    notificationContent.attachments = @[image];

iOS10之前通知的样式不能更改
在iOS10之后引入了UNNotificationationAttachment
可以在通知中添加图片,音频,视频
苹果对这些附件的大小和类型有一个限制:


文件大小限制

上文中的代码没有添加附件,这里附上效果图:

收到通知
下拉或者重压
关于创建附件方法中的options,请参考大神徐不同作者的文章
    //注册本地通知用到的Action
    //进入app按钮
    UNNotificationAction * localAction = [UNNotificationAction actionWithIdentifier:@"localAction" title:@"处理本地通知" options:UNNotificationActionOptionForeground];
    ///回复文本按钮
    UNTextInputNotificationAction * localText = [UNTextInputNotificationAction actionWithIdentifier:@"localText" title:@"本地文本" options:UNNotificationActionOptionNone];
    //取消按钮
    UNNotificationAction *localCancel = [UNNotificationAction actionWithIdentifier:@"localCancel" title:@"取消" options:UNNotificationActionOptionDestructive];
    //将这些action带入category
    UNNotificationCategory *localCategory = [UNNotificationCategory categoryWithIdentifier:@"localCategory" actions:@[localAction,localText,localCancel] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];

UNNotificationActionOptions是一个枚举:
UNNotificationActionOptionDestructive : 破坏性的,显示为红色
UNNotificationActionOptionNone : 不必打开app进行操作
UNNotificationActionOptionForeground : 打开app进行操作

触发器和通知内容内容最后形成UNNotificationRequest
直接交给通知中心进行发送
发送成功后
该通知会按照触发器的触发条件进行触发
并且会显示到通知中心上
用户可与指定的category交互方式与通知进行交互

//获取在Pending状态下待触发的通知
- (void)getPendingNotificationRequestsWithCompletionHandler:(void(^)(NSArray<UNNotificationRequest *> *requests))completionHandler;
//移除未触发的通知
- (void)removePendingNotificationRequestsWithIdentifiers:(NSArray<NSString *> *)identifiers;
- (void)removeAllPendingNotificationRequests;

// 通知已经触发,但是还在操作系统的通知中心上,可以进行查询和删除
- (void)getDeliveredNotificationsWithCompletionHandler:(void(^)(NSArray<UNNotification *> *notifications))completionHandler __TVOS_PROHIBITED;
- (void)removeDeliveredNotificationsWithIdentifiers:(NSArray<NSString *> *)identifiers __TVOS_PROHIBITED;
- (void)removeAllDeliveredNotifications __TVOS_PROHIBITED;

远程通知与本地通知的流程一样
只不过触发器是UNPushNotificationTrigger
并且不需要形成request
由Provider Service发送给APNs
APNs发送给苹果设备以后生成
在代理回调的函数中获取request

2.通知的监听:

苹果对iOS10中的通知监听方法进行了整合
并且把收到通知和点击通知分开来处理:
app在前台的时候,收到了通知
这时候 app 不会弹 alert
但是还是会走回调 :

- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{
   /** 根据触发器类型 来判断通知类型 */
    if ([request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        //远程通知处理
        NSLog(@"收到远程通知");
    }else if ([request.trigger isKindOfClass:[UNTimeIntervalNotificationTrigger class]]) {
        //时间间隔触发器通知处理
        NSLog(@"收到本地通知");
    }else if ([request.trigger isKindOfClass:[UNCalendarNotificationTrigger class]]) {
        //日历触发器通知处理
        NSLog(@"收到本地通知");
    }else if ([request.trigger isKindOfClass:[UNLocationNotificationTrigger class]]) {
        //位置触发器通知处理
        NSLog(@"收到本地通知");
    }
    /** 如果不想按照系统的方式展示通知,可以不传入UNNotificationPresentationOptionAlert,自定义弹窗 */
 completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionSound|UNNotificationPresentationOptionAlert);
}

app在后台收到通知
并且点击的时候调用 :

//用户与通知进行交互后的response,比如说用户直接点开通知打开App、用户点击通知的按钮或者进行输入文本框的文本
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler{
    //在此,可判断response的种类和request的触发器是什么,可根据远程通知和本地通知分别处理,再根据action进行后续回调
    if ([request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {//远程通知
        //可根据actionIdentifier来做业务逻辑
        if ([response isKindOfClass:[UNTextInputNotificationResponse class]]) {
            UNTextInputNotificationResponse * textResponse = (UNTextInputNotificationResponse*)response;
            NSString * text = textResponse.userText;
            NSLog(@"回复内容 : %@",text);
        }else{
            if ([response.actionIdentifier isEqualToString:@"remoteAction"]) {
                NSLog(@"点击了处理远程通知按钮");
            }
        }
    }else {//本地通知
        //可根据actionIdentifier来做业务逻辑
        if ([response isKindOfClass:[UNTextInputNotificationResponse class]]) {
            UNTextInputNotificationResponse * textResponse = (UNTextInputNotificationResponse*)response;
            NSString * text = textResponse.userText;
            NSLog(@"回复内容 : %@",text);
        }else{
            if ([response.actionIdentifier isEqualToString:@"localAction"]) {
                NSLog(@"点击了处理本地通知按钮");
            }
        }
    }
    completionHandler();
}

在 iOS10 之前
如果 app 是杀死状态
这个时候点击 push 进入 app
这时候并不走 appDelegate 的通知处理方法
而是在 LaunchingWithOptions 方法中的参数launchOptions中有一个 key 不为空

 let local = launchOptions![UIApplicationLaunchOptionsKey.localNotification]

在 iOS10 以后
把 app 杀死的情况和活跃在后台的情况统一了
都是走 appDelegate 的通知处理方法
但是 iOS10 以后 LaunchingWithOptions 方法中的参数 launchOptions 的哪个 key, 依然不为空

3.Extension:

新通知框架的两个Extension
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler;
- (void)serviceExtensionTimeWillExpire;

在Demo中,重写了第一个方法
在这个方法中
可以拿到通知
对通知进行一系列修改
然后回调给系统
让系统展示

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    // copy发来的通知,开始做一些处理
    self.bestAttemptContent = [request.content mutableCopy];
    
    // Modify the notification content here...
    self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];
    
    // 重写一些东西
    self.bestAttemptContent.title = @"我是标题";
    self.bestAttemptContent.subtitle = @"我是子标题";
    self.bestAttemptContent.body = @"我要写代码";
    
    // 这里添加一些点击事件,可以在收到通知的时候,添加,也可以在拦截通知的这个扩展中添加
    self.bestAttemptContent.categoryIdentifier = @"remoteCategory";
    
    //进行一系列自己的定制操作后 回调给系统:
    self.contentHandler(self.bestAttemptContent);
}

需要注意的是
如果创建了Notification Service Extension
并使用它
在推送体中必须加mutable-content字段
Notification Service Extension暂时只支持远程推送

{
    "aps": {
        "alert": "This is some fancy message.",
        "badge": 1,
        "sound": "default",
        "mutable-content": "1"
    }
}

在XCode菜单中选择File->New->Target,创建NotificationContent Extension
创建成功以后
项目中也多了一个文件夹:


NotificationContent Extension

本文Demo:

demo

感谢阅读
你的支持是我写作的唯一动力

上一篇 下一篇

猜你喜欢

热点阅读