iOS 10 消息推送(UserNotifications)秘籍

作者: Dely | 来源:发表于2016-09-28 11:37 被阅读31030次

前言

之前说会单独整理消息通知的内容,但是因为工(就)作(是)的(很)事(懒)没有更新文章,违背了自己的学习的初衷。因为互联网一定要有危机意识,说不定眼一睁,我们就out丢了饭碗。


图片来源网络.jpeg

“狼,他没有狮子老虎强壮,也没有大象那庞大的身躯,但至少:我从来没在马戏团看到过他们的身影。”

也许只有狼在一直奔跑,这是我一直喜欢它的原因,要像狼一样不断奔跑,才能幸存!

看完楼主装的一手好X,我来总结一点点你都知道的通知方面的知识点!

楼主装逼,打他.jpg

背景

iOS10 新特性一出,各个大神就早已研究新特性能给场景智能化所带来的好处(唉,可惜我只是一个小白)。我也被安排适配iOS10的推送工作!

Apple 表示这是 iOS 有史以来最大的升级(our biggest release yet),更加智能开放的 Siri 、强化应用对 3D Touch 支持、 HomeKit 、电话拦截及全新设计的通知等等…

iOS 10 中将之前繁杂的推送通知统一成UserNotifications.framework 来集中管理和使用通知功能,还增加一些实用的功能——撤回单条通知、更新已展示通知、中途修改通知内容、在通知中显示多媒体资源、自定义UI等功能,功能着实强大!

本文主要是针对iOS 10的消息通知做介绍,所以很多代码没有对iOS 10之前做添加适配。

基本原理

iOS推送分为Local Notifications(本地推送) 和 Remote Notifications(远程推送)(原理图来源于网络,如有侵权请告知,我会添加来源,我怕我赔不起)

Local Notifications(本地推送)

Local Notifications.png
  1. App本地创建通知,加入到系统的Schedule里,
  2. 如果触发器条件达成时会推送相应的消息内容

Remote Notifications(远程推送)

Remote Notifications1.jpg

图中,Provider是指某个iPhone软件的Push服务器,这篇文章我将使用我花了12块大洋(心疼)买的 APNS Pusher 作为我的推送源。

APNS 是Apple Push Notification Service(Apple Push服务器)的缩写,是苹果的服务器。

上图可以分为三个阶段:

第一阶段:APNS Pusher应用程序把要发送的消息、目的iPhone的标识打包,发给APNS。

第二阶段:APNS在自身的已注册Push服务的iPhone列表中,查找有相应标识的iPhone,并把消息发到iPhone。

第三阶段:iPhone把发来的消息传递给相应的应用程序, 并且按照设定弹出Push通知。

Remote Notifications2.jpeg

从上图我们可以看到:

  1. 首先是应用程序注册消息推送。

  2. IOS跟APNS Server要deviceToken。应用程序接受deviceToken。

  3. 应用程序将deviceToken发送给PUSH服务端程序。

  4. 服务端程序向APNS服务发送消息。

  5. APNS服务将消息发送给iPhone应用程序。

基本配置和基本方法

如果只是简单的本地推送,跳过1 2 步骤,直接到3

1、 如果你的App有远端推送的话,那你需要开发者账号的,需要新建一个对应你bundle的push 证书。证书这一块我就不说了,如果针对证书有什么问题可以给我留言,我会单独把证书相关的知识点整理起来!如果你没有账号,可以到某宝买个,很便宜。
2、 Capabilities中打开Push Notifications 开关
在XCode7中这里的开关不打开,推送也是可以正常使用的,但是在XCode8中,这里的开关必须要打开,不然会报错:

Error Domain=NSCocoaErrorDomain Code=3000 "未找到应用程序的“aps-environment”的授权字符串" UserInfo={NSLocalizedDescription=未找到应用程序的“aps-environment”的授权字符串}

打开后会自动在项目里生成entitlements文件。

Push Notification开关.png entitlements文件.png

3、 推送的注册

第一步: 导入 #import <UserNotifications/UserNotifications.h>
且要遵守<UNUserNotificationCenterDelegate>的协议,在Appdelegate.m中。
这里需要注意,我们最好写成这种形式(防止低版本找不到头文件出现问题)

#ifdef NSFoundationVersionNumber_iOS_9_x_Max
#import <UserNotifications/UserNotifications.h>
#endif

第二步:我们需要在
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions中注册通知,代码如下

  - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [self replyPushNotificationAuthorization:application];
    return YES;
}


#pragma mark - 申请通知权限
// 申请通知权限
- (void)replyPushNotificationAuthorization:(UIApplication *)application{
    
    if (IOS10_OR_LATER) {
        //iOS 10 later
        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
        //必须写代理,不然无法监听通知的接收与点击事件
        center.delegate = self;
        [center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError * _Nullable error) {
            if (!error && granted) {
                //用户点击允许
                NSLog(@"注册成功");
            }else{
                //用户点击不允许
                NSLog(@"注册失败");
            }
        }];
        
        // 可以通过 getNotificationSettingsWithCompletionHandler 获取权限设置
        //之前注册推送服务,用户点击了同意还是不同意,以及用户之后又做了怎样的更改我们都无从得知,现在 apple 开放了这个 API,我们可以直接获取到用户的设定信息了。注意UNNotificationSettings是只读对象哦,不能直接修改!
        [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
            NSLog(@"========%@",settings);
        }];
    }else if (IOS8_OR_LATER){
        //iOS 8 - iOS 10系统
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
        [application registerUserNotificationSettings:settings];
    }else{
        //iOS 8.0系统以下
        [application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound];
    }
    
    //注册远端消息通知获取device token
    [application registerForRemoteNotifications];
}

上面需要注意:

1. 必须写代理,不然无法监听通知的接收与点击事件
 center.delegate = self;

下面是我在项目里定义的宏
#define IOS10_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10.0)
#define IOS9_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0)
#define IOS8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
#define IOS7_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0)

2. 之前注册推送服务,用户点击了同意还是不同意,以及用户之后又做了怎样的更改我们都无从得知,现在 apple 开放了这个 API,我们可以直接获取到用户的设定信息了。注意UNNotificationSettings是只读对象哦,不能直接修改!只能通过以下方式获取
 [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
            NSLog(@"========%@",settings);
  }];
打印信息如下:
========<UNNotificationSettings: 0x1740887f0; authorizationStatus: Authorized, notificationCenterSetting: Enabled, soundSetting: Enabled, badgeSetting: Enabled, lockScreenSetting: Enabled, alertSetting: NotSupported, carPlaySetting: Enabled, alertStyle: Banner>

4、 远端推送需要获取设备的Device Token的方法是没有变的,代码如下

#pragma  mark - 获取device Token
//获取DeviceToken成功
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
    
    //解析NSData获取字符串
    //我看网上这部分直接使用下面方法转换为string,你会得到一个nil(别怪我不告诉你哦)
    //错误写法
    //NSString *string = [[NSString alloc] initWithData:deviceToken encoding:NSUTF8StringEncoding];
    
    
    //正确写法
    NSString *deviceString = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    deviceString = [deviceString stringByReplacingOccurrencesOfString:@" " withString:@""];
    
    NSLog(@"deviceToken===========%@",deviceString);
}

//获取DeviceToken失败
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
    NSLog(@"[DeviceToken Error]:%@\n",error.description);
}

5、这一步吊了,这是iOS 10系统更新时,苹果给了我们2个代理方法来处理通知的接收和点击事件,这两个方法在<UNUserNotificationCenterDelegate>的协议中,大家可以查看下。

@protocol UNUserNotificationCenterDelegate <NSObject>

@optional

// The method will be called on the delegate only if the application is in the foreground. If the method is not implemented or the handler is not called in a timely manner then the notification will not be presented. The application can choose to have the notification presented as a sound, badge, alert and/or in the notification list. This decision should be based on whether the information in the notification is otherwise visible to the user.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);

// The method will be called on the delegate when the user responded to the notification by opening the application, dismissing the notification or choosing a UNNotificationAction. The delegate must be set before the application returns from applicationDidFinishLaunching:.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0) __TVOS_PROHIBITED;

@end


此外,苹果把本地通知跟远程通知合二为一。区分本地通知跟远程通知的类是UNPushNotificationTrigger.h类中,UNPushNotificationTrigger的类型是新增加的,通过它,我们可以得到一些通知的触发条件 ,解释如下:

  1. UNPushNotificationTrigger (远程通知) 远程推送的通知类型
  2. UNTimeIntervalNotificationTrigger (本地通知) 一定时间之后,重复或者不重复推送通知。我们可以设置timeInterval(时间间隔)和repeats(是否重复)。
  3. UNCalendarNotificationTrigger(本地通知) 一定日期之后,重复或者不重复推送通知 例如,你每天8点推送一个通知,只要dateComponents为8,如果你想每天8点都推送这个通知,只要repeats为YES就可以了。
  4. UNLocationNotificationTrigger (本地通知)地理位置的一种通知,
    当用户进入或离开一个地理区域来通知。
    现在先提出来,后面我会一一代码演示出每种用法。还是回到两个很吊的代理方法吧
#pragma mark - iOS10 收到通知(本地和远端) UNUserNotificationCenterDelegate

//App处于前台接收通知时
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{
    
    //收到推送的请求
    UNNotificationRequest *request = notification.request;
    
    //收到推送的内容
    UNNotificationContent *content = request.content;
    
    //收到用户的基本信息
    NSDictionary *userInfo = content.userInfo;
    
    //收到推送消息的角标
    NSNumber *badge = content.badge;
    
    //收到推送消息body
    NSString *body = content.body;
    
    //推送消息的声音
    UNNotificationSound *sound = content.sound;
    
    // 推送消息的副标题
    NSString *subtitle = content.subtitle;
    
    // 推送消息的标题
    NSString *title = content.title;
    
    if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        //此处省略一万行需求代码。。。。。。
        NSLog(@"iOS10 收到远程通知:%@",userInfo);
        
    }else {
        // 判断为本地通知
        //此处省略一万行需求代码。。。。。。
        NSLog(@"iOS10 收到本地通知:{\\\\nbody:%@,\\\\ntitle:%@,\\\\nsubtitle:%@,\\\\nbadge:%@,\\\\nsound:%@,\\\\nuserInfo:%@\\\\n}",body,title,subtitle,badge,sound,userInfo);
    }
    

    // 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Alert三种类型可以设置
    completionHandler(UNNotificationPresentationOptionBadge|
                      UNNotificationPresentationOptionSound|
                      UNNotificationPresentationOptionAlert);
    
}
    

//App通知的点击事件
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler{
    //收到推送的请求
    UNNotificationRequest *request = response.notification.request;
    
    //收到推送的内容
    UNNotificationContent *content = request.content;
    
    //收到用户的基本信息
    NSDictionary *userInfo = content.userInfo;
    
    //收到推送消息的角标
    NSNumber *badge = content.badge;
    
    //收到推送消息body
    NSString *body = content.body;
    
    //推送消息的声音
    UNNotificationSound *sound = content.sound;
    
    // 推送消息的副标题
    NSString *subtitle = content.subtitle;
    
    // 推送消息的标题
    NSString *title = content.title;
    
    if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        NSLog(@"iOS10 收到远程通知:%@",userInfo);
        //此处省略一万行需求代码。。。。。。
        
    }else {
        // 判断为本地通知
        //此处省略一万行需求代码。。。。。。
        NSLog(@"iOS10 收到本地通知:{\\\\nbody:%@,\\\\ntitle:%@,\\\\nsubtitle:%@,\\\\nbadge:%@,\\\\nsound:%@,\\\\nuserInfo:%@\\\\n}",body,title,subtitle,badge,sound,userInfo);
    }

    //2016-09-27 14:42:16.353978 UserNotificationsDemo[1765:800117] Warning: UNUserNotificationCenter delegate received call to -userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: but the completion handler was never called.
    completionHandler(); // 系统要求执行这个方法
}

需要注意的:

1.下面这个代理方法,只会是app处于前台状态 前台状态 and 前台状态下才会走,后台模式下是不会走这里的
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler


2.下面这个代理方法,只会是用户点击消息才会触发,如果使用户长按(3DTouch)、弹出Action页面等并不会触发。点击Action的时候会触发!
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler

3.点击代理最后需要执行:completionHandler(); // 系统要求执行这个方法
不然会报:
2016-09-27 14:42:16.353978 UserNotificationsDemo[1765:800117] Warning: UNUserNotificationCenter delegate received call to -userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: but the completion handler was never called.

4.不管前台后台状态下。推送消息的横幅都可以展示出来!后台状态不用说,前台时需要在前台代理方法中设置 ,设置如下:
// 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Alert三种类型可以设置
completionHandler(UNNotificationPresentationOptionBadge|
                  UNNotificationPresentationOptionSound|
                  UNNotificationPresentationOptionAlert);

6、 iOS 10之前接收通知的兼容方法

#pragma mark -iOS 10之前收到通知

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    NSLog(@"iOS6及以下系统,收到通知:%@", userInfo);
    //此处省略一万行需求代码。。。。。。
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    NSLog(@"iOS7及以上系统,收到通知:%@", userInfo);
    completionHandler(UIBackgroundFetchResultNewData);
    //此处省略一万行需求代码。。。。。。
}

段结:是不是以为就结束了?NO NO NO(你以为离开了幻境,其实才刚刚踏入幻境!)上面的介绍了基本原理、基本配置以及基本方法说明,现在做完这些工作,我们的学习才刚刚开始!现在天时、地利、人和、可以开始下面推送coding的学习和测试了。

在用户日常生活中会有很多种情形需要通知,比如:新闻提醒、定时吃药、定期体检、到达某个地方提醒用户等等,这些功能在 UserNotifications 中都提供了相应的接口。

图片来源于网络.jpeg

我们先学会基本的技能简单的推送(爬),后面在学习进阶定制推送(走),最后看看能不能高级推送(飞不飞起来看个人了,我是飞不起来):

基本Local Notifications(本地推送) 和 Remote Notifications(远程推送)

一、 基本的本地推送

本地推送生成主要流程就是:

1. 创建一个触发器(trigger)
2. 创建推送的内容(UNMutableNotificationContent)
3. 创建推送请求(UNNotificationRequest)
4. 推送请求添加到推送管理中心(UNUserNotificationCenter)中

1、新功能trigger可以在特定条件触发,有三类:UNTimeIntervalNotificationTrigger、UNCalendarNotificationTrigger、UNLocationNotificationTrigger

1.1、 UNTimeIntervalNotificationTrigger:一段时间后触发(定时推送

//timeInterval:单位为秒(s)  repeats:是否循环提醒
//50s后提醒
UNTimeIntervalNotificationTrigger *trigger1 = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:50 repeats:NO];

1.2 UNCalendarNotificationTrigger :调用
+ (instancetype)triggerWithDateMatchingComponents:(NSDateComponents *)dateComponents repeats:(BOOL)repeats;进行注册;时间点信息用 NSDateComponents.(定期推送

//在每周一的14点3分提醒
NSDateComponents *components = [[NSDateComponents alloc] init]; 
components.weekday = 2;
components.hour = 16;
components.minute = 3;
 // components 日期
UNCalendarNotificationTrigger *calendarTrigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:YES];

1.3、UNLocationNotificationTrigger:调用
+ (instancetype)triggerWithRegion:(CLRegion *)region repeats:(BOOL)repeats;
进行注册,地区信息使用CLRegion的子类CLCircularRegion,可以配置region属性 notifyOnEntrynotifyOnExit,是在进入地区、从地区出来或者两者都要的时候进行通知,这个测试过程专门从公司跑到家时刻关注手机有推送嘛,果然是有的(定点推送

  //首先得导入#import <CoreLocation/CoreLocation.h>,不然会regin创建有问题。
  // 创建位置信息
  CLLocationCoordinate2D center1 = CLLocationCoordinate2DMake(39.788857, 116.5559392);
  CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:center1 radius:500 identifier:@"经海五路"];
  region.notifyOnEntry = YES;
  region.notifyOnExit = YES;
  // region 位置信息 repeats 是否重复 (CLRegion 可以是地理位置信息)
  UNLocationNotificationTrigger *locationTrigger = [UNLocationNotificationTrigger triggerWithRegion:region repeats:YES];

2、创建推送的内容(UNMutableNotificationContent)
UNNotificationContent:属性readOnly
UNMutableNotificationContent:属性有title、subtitle、body、badge、sound、lauchImageName、userInfo、attachments、categoryIdentifier、threadIdentifier

本地消息内容 |内容限制大小 |展示
-------------|--------|
title |NSString| 限制在一行,多出部分省略号
subtitle |NSString |限制在一行,多出部分省略号
body| NSString |通知栏出现时,限制在两行,多出部分省略号;预览时,全部展示

**注意点: **body中printf风格的转义字符,比如说要包含%,需要写成%% 才会显示,\同样

    // 创建通知内容 UNMutableNotificationContent, 注意不是 UNNotificationContent ,此对象为不可变对象。
    UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
    content.title = @"Dely 时间提醒 - title";
    content.subtitle = [NSString stringWithFormat:@"Dely 装逼大会竞选时间提醒 - subtitle"];
    content.body = @"Dely 装逼大会总决赛时间到,欢迎你参加总决赛!希望你一统X界 - body";
    content.badge = @666;
    content.sound = [UNNotificationSound defaultSound];
    content.userInfo = @{@"key1":@"value1",@"key2":@"value2"};

3、创建完整的本地推送请求Demo

//定时推送
+ (void)createLocalizedUserNotification{
    
    // 设置触发条件 UNNotificationTrigger
    UNTimeIntervalNotificationTrigger *timeTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5.0f repeats:NO];
       
    // 创建通知内容 UNMutableNotificationContent, 注意不是 UNNotificationContent ,此对象为不可变对象。
    UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
    content.title = @"Dely 时间提醒 - title";
    content.subtitle = [NSString stringWithFormat:@"Dely 装逼大会竞选时间提醒 - subtitle"];
    content.body = @"Dely 装逼大会总决赛时间到,欢迎你参加总决赛!希望你一统X界 - body";
    content.badge = @666;
    content.sound = [UNNotificationSound defaultSound];
    content.userInfo = @{@"key1":@"value1",@"key2":@"value2"};
    
    // 创建通知标示
    NSString *requestIdentifier = @"Dely.X.time";
    
    // 创建通知请求 UNNotificationRequest 将触发条件和通知内容添加到请求中
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:requestIdentifier content:content trigger:timeTrigger];
    
    UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
    // 将通知请求 add 到 UNUserNotificationCenter
    [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
        if (!error) {
            NSLog(@"推送已添加成功 %@", requestIdentifier);
            //你自己的需求例如下面:
            UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"本地通知" message:@"成功添加推送" preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
            [alert addAction:cancelAction];
            [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
            //此处省略一万行需求。。。。
        }
    }];

}

运行结果如下:

装X决赛通知.jpg

二、 基本的远端推送
如果你想模拟远端推送,按照我前面介绍的配置基本环境、证书、push开关和基本方法就可以模拟远端的基本远端推送。
1、运行工程则会拿到设备的Device Token,后面会用到。

device token.png

2、现在我们需要一个推送服务器给APNS发送信息。我前面说了我花了12块大洋(心疼死我了)买了一个APNS pusher 来模拟远端推送服务,当然你可以不花钱也可以用到,例如:
NWPusher

APNS pusher

3、你需要把你刚刚获取的device token填到相应位置,同时你要配置好push证书哦。

4、需要添加aps内容了,然后点击send就OK了

{
  "aps" : {
    "alert" : {
      "title" : "iOS远程消息,我是主标题!-title",
      "subtitle" : "iOS远程消息,我是主标题!-Subtitle",
      "body" : "Dely,why am i so handsome -body"
    },
    "badge" : "2"
  }
}

5、稍纵即逝你就收到了远端消息了

远端消息.jpg

6、Notification Management
对推送进行查、改、删。都需要一个必需的参数requestIdentifier

1、更新通知

Local Notification需要通过更新request.相同的requestIdentifier,重新添加到推送center就可以了,说白了就是重新创建local Notification request(只要保证requestIdentifier就ok了),应用场景如图

Local Notification更新前.png Local Notification更新后.png

Remote Notification 更新需要通过新的字段apps-collapse-id来作为唯一标示,我前面用的APNS pusher暂不支持这个字段,不过github上有很多这样的工具:
https://github.com/KnuffApp/Knuff
这样remote 也可以更新推送消息

2、推送消息的查找和删除

// Notification requests that are waiting for their trigger to fire
//获取未送达的所有消息列表
- (void)getPendingNotificationRequestsWithCompletionHandler:(void(^)(NSArray<UNNotificationRequest *> *requests))completionHandler;
//删除所有未送达的特定id的消息
- (void)removePendingNotificationRequestsWithIdentifiers:(NSArray<NSString *> *)identifiers;
//删除所有未送达的消息
- (void)removeAllPendingNotificationRequests;

// Notifications that have been delivered and remain in Notification Center. Notifiations triggered by location cannot be retrieved, but can be removed.
//获取已送达的所有消息列表
- (void)getDeliveredNotificationsWithCompletionHandler:(void(^)(NSArray<UNNotification *> *notifications))completionHandler __TVOS_PROHIBITED;
//删除所有已送达的特定id的消息
- (void)removeDeliveredNotificationsWithIdentifiers:(NSArray<NSString *> *)identifiers __TVOS_PROHIBITED;
//删除所有已送达的消息
- (void)removeAllDeliveredNotifications __TVOS_PROHIBITED;

测试如下:

+  (void)notificationAction{
    NSString *requestIdentifier = @"Dely.X.time";
    UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
    
    //删除设备已收到的所有消息推送
   // [center removeAllDeliveredNotifications];
    
    //删除设备已收到特定id的所有消息推送
   // [center removeDeliveredNotificationsWithIdentifiers:@[requestIdentifier]];
    
    //获取设备已收到的消息推送
    [center getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
    }];
}

段结: 收到通知时你需要在appdelegate里面的代理方法里处理你的需求逻辑,这个需要你自己写了。到目前为止你掌握了基本的本地推送基本的远端推送!

不知不觉写了这么多字(全是TM废话)、本来继续打算写进阶的本地和远端推送(Media Attachments、Notification Actions、自定义推送界面等),留着下一篇博客继续分享吧,欲知后事如何,且听下会装X!

如果你喜欢可以点个喜欢_(竟有如此厚颜无耻之人)

下集预告:


推送图片.jpg 推送图片2.jpg

参考资料:
https://developer.apple.com/reference/usernotifications
http://www.jianshu.com/p/b74e52e866fc
http://www.jianshu.com/p/b74e52e866fc
http://blog.csdn.net/he317165264/article/details/52574934
http://qoofan.com/read/PnEaMEZonD.html
http://www.qingpingshan.com/rjbc/ios/140921.html

相关文章

网友评论

  • PGOne爱吃饺子:大佬,问你一下啊,设置日历形式的推送,如果在还没有推送之前,就把他取消掉了,那么明天会不会继续推送啊,repeat我设置的为yes
  • 小丑_3bd1:楼主大神真是才华风趣幽默
  • WebbWang0323:怎么实现每天定期推送信息?上面只写了周一的、
    PGOne爱吃饺子:把周一去掉就可以了
  • PGOne爱吃饺子:首先赞美一下楼主,另外问一下楼主,是不是在iOS10之后,处理通知的方式分为前台和后台了,而且还单独给了两个新的代理方法,那么在ISO10的手机上,之前这个处理通知的方法是不是不走了
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:请楼主赐教一下
    PGOne爱吃饺子:@Dely 好的 非常感谢啊,谢谢啊
    Dely:远端推送通知,iOS 10后有这个前后台之分的,我所指的后台是程序是kill的状态,并不是切换home键退到后台。kill的状态下,收到消息是没有代理方法的,只有点击消息的时候会走- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler代理方法。- (void)application:(UIApplication *)application didReceiveRemoteNotification:方法是在iOS 10中废弃的方法,肯定不会走的:blush:
  • UASHS:清晰明了,赞一个
  • 65e6461658c5:本地推送是否会经过APNS ? 我是免费账户,用的XCode8.3 在IOS10上打算用cordova的本地通知插件自己给自己推送一条消息,但是没有任何反应,也没错误,不知道是插件的问题还是免费账户的问题。
  • Xir_Guitar:也可以给我一个证书吗??楼主,我是初学者,买个账号,又不划算 wx:834389217---
    NSLog(@"谢谢");
  • 单纯的敲代码:纠正一下,当没写didReceiveRemoteNotification:fetchCompletionHandler时,didReceiveRemoteNotification方法iOS10以下都会走
    当同时写了didReceiveRemoteNotification:fetchCompletionHandler时,iOS7以上iOS10以下将只走didReceiveRemoteNotification:fetchCompletionHandler,didReceiveRemoteNotification方法iOS7以下才会走
    Dely:@单纯的敲代码 谢谢指出
  • 搬砖小工:本地推送 后台好像不从顶部弹出提示框 这是为什么?
    搬砖小工:@不将就丶莫强求 后台和杀死app应该是可以收到本地推送的 原因是 后台程序挂起了 不执行添加代码。顺便问下怎么才能 长时间活着后台?
    搬砖小工:怎样才能保证后台长时间运行?超过三分钟。。。而且定时推送的话 杀掉APP 也有嘛?测试能成功嘛?
  • 爱阿爸的阿龙龙:写的挺好呀 ,今天刚好用到了 特意回来给楼主点个赞 哈哈
  • 2c7bbddaad99:楼主,请教个问题,按了home键,app在后台的情况下,还能接收到本地消息推送吗
    Dely:可以的啊
  • cba49443c5a9:请教楼主一个问题,同一个app有多条苹果推送通知,点开其中一条,如果让其余该app的通知消息不消失呢?
  • fdfbd575499a:楼主,求 demo 一份啊。895168068@qq.com
  • 愚人船ios:请问在后台收到推送的时候 还没点击的情况下怎么统计设置角标?iOS 10
  • Nick菠萝:写的很不错~
  • 808272bf8542:写的不错,很详细,谢谢楼主
  • all_2019:请问 iOS10接受不到推送怎么回事呀?推送证书配置了 推送工具推送的可以收到 但是后台给的就收不到呀。
  • oldmonster:对账号方面有点问题想要请教一下。
    目标:给应用添加消息推送。以下有关证书环境均是开发环境。
    1、新增证书的时候是选择iOS App Development还是Apple Push Notification service SSL (Sandbox)。我原来听人说选择前者即可,只需要在appid中设置消息推送即可,可是我做了之后发现不可行。
    2、我所在的公司用的是公司账号,只是把我添加到了开发者当中。而我在新建了证书之后在Xcode中选择我的账号的时候在设置中不会出现消息推送的开关,而使用公司账号时消息推送的开关才会出现。这是因为我的账号只是添加删除证书而一旦需要使用其他权限(如消息推送)就要使用公司证书去调试?
  • doubleJJ:不错 写得挺详细的
  • 流星大石头:感谢楼主!{
    "aps" : {
    "alert" : {
    "title" : "iOS远程消息,我是主标题!-title",
    "subtitle" : "iOS远程消息,我是主标题!-Subtitle",
    "body" : "Dely,why am i so handsome -body"
    },
    "badge" : "2"
    }
    }
    这里的badge应该是整数,不应该是字符串。如果是字符串,app角标就不会自动更新了
  • 7a946bf5d2ca:因为狼是群居,驯养很难。而且从狼的体形和技术来说没有可观赏的开发价值,将一匹狼驯养好,最后它只能像一只狗,
    UASHS:博主只是想突出狼的那种品性,不必这么非黑即白
    7a946bf5d2ca:这就是为什么马戏团看不到 狼。。。
    7a946bf5d2ca:马戏团要挣钱养活自己和那些动物,从经济学上来分析的话,
    训一匹狼是要付出成本的,而市场对狼的需求绝对是没有老虎和狮子那么大的,广大群众更愿意看老虎和狮子表演,狼本来和狗一家,哪个喜欢去看,你或许会想,买张票看“狗“耍,多亏,大家的不 去看,那他马戏团吃什么。所以马戏团里没的训狼并不是狼训不了,而是马戏团不做亏本生意罢了
  • b070694b63d5:厉害了我的歌~
    楼主有demo么, 有能不能发一份给我603256822@qq.com
    :+1:
  • o0阿拉斯加的狗0o:@Dely 博主,iOS10在前台收到推送会走这个方法,-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler,如果是在后台,收到推送,会走哪个方法呢?在线等
    2c7bbddaad99:同问,请问有解决方法吗
  • 8a75697ac973:你好啊 ,我也刚刚开始做IOS,证书这个搞死我了,给Apple打电话,最多也给我一个官方的地址,可是都是洋码子。能不能把你整理的证书资料给我 :stuck_out_tongue_closed_eyes: 可以发我邮箱:frog198306@gmail.com 谢谢 🙏
  • Steven_2016:6666666666666的不行不行的~~ :+1:
  • Living_U:666有没有原码啊 大牛
  • AFCLAY:up写的很棒.
    在后台的时候,点击通知,会触发didReceiveNotificationResponse.
    但是程序在前台时, willPresentNotification不调用是为什么?
  • AaronJobs:有一个疑问 直接引入<UserNotifications/UserNotifications.h> 设置遵守unusernotificationcenterdelegate,如果运行在iOS10以下的低版本设备上会不会造成应用崩溃的问题?求解答啊 :sob:
    AaronJobs:@Dely 感激大神, 引入头文件跟协议不会崩溃这个点get到了,不过我写了一个格式化推送通知的方法 ,
    -(NSDictionary*)formatNotificationFromNotificationDic:(NSDictionary*)notificationDic orNotification:(UNNotification*)notification;
    本来的想法是 iOS10之下接到推送消息就传入userInfo iOS10推送消息就传入notification ,这种情况下 如果是iOS10之下的系统是不是就会崩溃了 ,求指导 :sob:
    Dely:@AaronJobs 放心不会崩溃 :smile:
  • Z了个Y:顶 不错
  • 爱情公寓:楼主的很详细 先感谢下。不过我这里设置时间有点问题 求楼主指点啊
    爱情公寓:@Dely 我想在一个周期内设置那天的某个时刻推送信息。用本地推送。比如从今天起。在这周内每天8点推送。楼主咋整
    Dely:@爱情公寓 什么问题可以私信我 :smile:
  • 若雨千寻:楼主,怎么让手机收到远程通知多震动几次呢?就想手机扣扣进程杀掉后收到视频或者语音通话邀请那样!
  • 树深不闻钟:问一下自定义推送消息可以添加视频吗?
    Dely:@陈迟Jz 可以啊,官方文档里面有支持类型:smile:
  • 不停歇的蜗牛:我就喜欢装逼的人 :smile: :smiley:
  • Thebloodelves:很吊,给我打,虽然都在用就是没有这么详细的了解过
    Dely:@Thebloodelves :relieved:
  • 和影子玩拳击:大神,我的推送在测试环境收不到,打包到AppStore能收到,知道是什么原因吗?
    和影子玩拳击:@Dely 证书的话,用的是Xcode8自动设置的那个 :cold_sweat:
    Dely:@和影子玩拳击 检查一下开发推送证书,可能你的测试环境证书配置的有问题 :smile:
  • 3c01a04c0bc3:谢谢分享,崇拜你
  • a5d3ea5ca61c:楼主写的不错,不过有个地方不太准确
    2.下面这个代理方法,只会是用户点击消息才会触发,如果使用户长按(3DTouch)、Action等并不会触发。
    Action时是会触发的,包括foreground时
    a5d3ea5ca61c:@Dely 应该是只有didReceiveNotificationResponse时才会报警告,willPresentNotification不会报的
    Dely:@muer2000 谢谢你的指正,弹出Action的页面时候并不会触发,点击Action的时候会触发, completionHandler的确不是必须的,系统会报警告!只是我们想把警告去掉 :smile:
    a5d3ea5ca61c:另外 willPresentNotification 的 completionHandler回调也不是必须的
  • 8e83c41d498d: 你好棒哦,就是我的偶像!
    酷哥不回头看爆炸:@ygcutter 我怎么收到你的评论了,bug啊
  • NateLam:即将要写通知了, 谢谢你
  • 踏浪帅:<UNUserNotificationCenterDelegate> 这个协议是IOS10 才有吧?那你们是怎么兼容? 就是引入这个协议,比如我在IOS8就让它不要报错
    AaronJobs:@Dely 引入头文件跟协议不会崩溃这个点get到了,不过我写了一个格式化推送通知的方法 ,
    -(NSDictionary*)formatNotificationFromNotificationDic:(NSDictionary*)notificationDic orNotification:(UNNotification*)notification;
    本来的想法是 iOS10之下接到推送消息就传入userInfo iOS10推送消息就传入notification ,这种情况下 如果是iOS10之下的系统是不是就会崩溃了 ,求指导 :sob:
    AaronJobs:@Dely :+1: :+1: :+1: 太赞了 太赞了 太赞了
    Dely:@踏浪帅 不会报错的,oc是运行时机制的,iOS8的是不会走代理的,也不会因为找不到代理方法而报错!
  • 浮浅丶Superficial:好详细,学习学习
    Dely:@浮浅丶Superficial 谢谢
  • 柏斯特湾:支持先驱者!
    Dely:@柏斯特湾 谢谢支持:smile:
  • 舒耀:good!那个你有试过再通知上添加3D Touch吗?怎么实现,没思路啊,请教一下
    舒耀:@Dely OK在看你的第二篇了,很感谢,这几天正好涉及到这个
    Dely:@舒耀 通知如果支持3Dtouch会用3Dtouch方式来触发,不支持的话右划有选项显示和清除,通知里没法加touch哦,因为自定义通知页面没法响应事件:smile:
  • 曾經蠟筆沒有小新:厉害呢,我的哥
  • 墨色褪尽:很强大,很详细,干货,收了,3Q
    Dely:@墨色褪尽 谢谢认可:smile:
  • 申经炳Ewane:小马扎拿来,已经坐好。要和博主一起学习装逼技巧🌚
    Dely:@申经炳 那就一起来哦:relieved:
  • 1347d391f7db:期待(下):+1:
    Dely:@夏小零 看看有没有让你失望:pensive:
  • MatthewSp:我要跟楼主学装逼
    Dely:@MatthewSp 那就一起来啊:smile:
  • Python数据分析实战:本地推送那个怎么没显示,按照你的代码写的
    Dely:@SilenceYaung 下面博客有DEMO链接,可以看看代码:relieved:
  • mqw:可以,你打动了我,我要跟你学装逼! :clap:
    Dely:@会飞的哈士奇 那就来吧:smile:
  • 若雨千寻:楼主何时再次装逼
    若雨千寻:@Dely 马克和刘明是对好朋友!
    卟师:@Dely 已经搬好板凳,备好花生瓜子啤酒,坐等观望
    Dely:@若雨千寻 正在谋划:relieved:
  • 卟师:我能转载分享吗?我会标注上作者和出处的
    Dely:@卟师 可以的:smile:
  • 小白白又白:后退我要开始装逼了
    Dely:@873d09120c94 一起:stuck_out_tongue:
  • 68c2930b2469: 能给一份Demo吗楼主?820533902@qq.com谢谢 :blush:
  • LeoZzz:666
    Dely:@GlKing 谢谢认可
  • 酷哥不回头看爆炸:很详细。
    酷哥不回头看爆炸:@ygcutter 粉丝。你要我的签名吗?
    8e83c41d498d:你好棒哦,就是我的偶像!
    Dely:@无言是非丶 谢谢:relieved:
  • 5cdc17ac17cc:有demo嘛?
  • 梦想总是美好的:tm的。我还没发表睡着了,你拿去发表了。。我就弄死你
    Dely:@梦想总是美好的 这么垃圾的文章怎么可能是大神你写的!你写的肯定比我好10000的10000次方倍啊 :wink:
  • Auditore:可以 很屌
    Dely:@Auditore 没有的事,我只是整理一下你们都知道的 :relaxed:
  • 帅气的小时:很强势很有用 :smile:
    Dely:@帅气的小时 谢谢支持 :blush:
  • loohcs:厉害,赞一个👍
    Dely:@loohcs 谢谢 :grin:
  • MakeThatChange:楼主 我想练练上架流程 但是没有账号 0 0 ,可以麻花腾我嘛 904885694
    Dely:@MakeThatChange 需要你手机的UDID才能加!可以加我微信Delyer521 :smile:
    MakeThatChange:@Dely jiang293172@163.com. 谢谢楼主
    Dely:@MakeThatChange 我这账号不是我的,我可以把你手机加入开发者把证书发给你用来测试。账号密码给你出问题我要负责人的!:joy:
  • 晓叶:厉害 :+1:
    Dely:@656565 不行啊,我会骄傲的,下面的总结稍微麻烦点!希望共同学习:smile:
  • 25e7864b9e8d:可以啊
    Dely:@hejiyang 没有的事:pensive:
  • JaryWang:nice
    Dely:@JaryWang 谢谢支持:stuck_out_tongue:
  • 行走在北方:写的详细,:+1::+1:
    Dely:@行走在北方 谢谢认可:relieved:
  • 半碗大米汤:很全:+1:
    Dely:@cwli 谢谢支持,还没写完,下面继续整理学习进阶的推送 :smile:
  • 荷洛图: :+1:
    Dely:@荷洛图 谢谢支持 :relaxed:
  • 橡树fulen:吊吊吊
    Dely:@橡树fuller 不行,不行 :smile:

本文标题:iOS 10 消息推送(UserNotifications)秘籍

本文链接:https://www.haomeiwen.com/subject/iovhyttx.html