说明
此文以极光推送来举例,相信其它第三方原理相同.
支持到 iOS10.2
全部为笔者今天测试结论,如有变化,请注意日期
测试环境:
Xcode 8.2,
iOS 10.2 & iOS 9.2.1
-
本系列包括三大部分:
头文件
#import "AppDelegate.h"
#import "JPUSHService.h"
// iOS10注册APNs所需头文件
#ifdef NSFoundationVersionNumber_iOS_9_x_Max
#import <UserNotifications/UserNotifications.h>
#endif
//JPush
static NSString *const appKey = @"5a6e94518f6bd85ba173cf77";
static NSString *const channel = @"App Store";
static BOOL const isProduction = YES;
@interface AppDelegate ()<JPUSHRegisterDelegate>
@end
辅助测试的UI代码:
#pragma mark --------------------------------------------------------
#pragma mark observe & addView
- (void)networkDidReceiveMsg:(NSNotification *)note{
// NSDictionary * userInfo = [note userInfo];
// NSString *content = [userInfo valueForKey:@"content"];
// NSDictionary *extras = [userInfo valueForKey:@"extras"];
// NSString *customizeField1 = [extras valueForKey:@"customizeField1"]; //服务端传递的Extras附加字段,key是自己定义的
NSString *text = @"NSNotification";
[self addTestViewRed:text];
}
- (void)addTestViewYellow:(NSString *)text{
UIView *testView = [[UIView alloc] init];
testView.backgroundColor = [[UIColor yellowColor] colorWithAlphaComponent:0.5];
testView.yh_size = CGSizeMake(100, 100);
testView.center = [UIApplication sharedApplication].keyWindow.center;
UITextView *textView = [[UITextView alloc] init];
textView.bounces = YES;
textView.text = text;
textView.frame = testView.bounds;
textView.yh_height -= 20;
[testView addSubview:textView];
[self.window.rootViewController.view addSubview:testView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(testGes:)];
[testView addGestureRecognizer:tap];
}
- (void)addTestViewRed:(NSString *)text{
UIView *testView = [[UIView alloc] init];
testView.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.5];
testView.yh_size = CGSizeMake(100, 100);
testView.center = self.window.center;
UITextView *textView = [[UITextView alloc] init];
textView.bounces = YES;
textView.text = text;
textView.frame = testView.bounds;
textView.yh_height -= 20;
[testView addSubview:textView];
[self.window.rootViewController.view addSubview:testView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(testGes:)];
[testView addGestureRecognizer:tap];
}
- (void)testGes:(UITapGestureRecognizer *)tapGes{
[tapGes.view removeFromSuperview];
}
注册代码:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
application.applicationIconBadgeNumber = 0;
//设置推送
[self setupJPush:launchOptions];
return YES;
}
//建立JPush
- (void)setupJPush:(NSDictionary *)launchOptions{
//Required
if ([[UIDevice currentDevice].systemVersion floatValue] >= 10.0) {
JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];
entity.types = UNAuthorizationOptionAlert|UNAuthorizationOptionBadge|UNAuthorizationOptionSound;
[JPUSHService registerForRemoteNotificationConfig:entity delegate:self];
}
else if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
//可以添加自定义categories
[JPUSHService registerForRemoteNotificationTypes:(UIUserNotificationTypeBadge |
UIUserNotificationTypeSound |
UIUserNotificationTypeAlert)
categories:nil];
} else {
//categories 必须为nil
[JPUSHService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeSound |
UIRemoteNotificationTypeAlert)
categories:nil];
}
//Required
[JPUSHService setupWithOption:launchOptions appKey:appKey
channel:channel
apsForProduction:isProduction];
///极光文档:只有在前端运行的时候才能收到自定义消息的推送。
//笔者并没有测试到执行这个通知方法
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkDidReceiveMsg:) name:kJPFNetworkDidReceiveMessageNotification object:nil];
if (launchOptions) {
NSString *alert = launchOptions[@"aps"][@"alert"];
NSString *string = [NSString stringWithFormat:@"%@%@", @"有通知: setupJPush", alert];
[self addTestViewRed:string];
}
}
处理推送的各方法
#pragma mark ------------------------------------------
#pragma mark JPush methods
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
/// Required - 注册 DeviceToken
[JPUSHService registerDeviceToken:deviceToken];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSString *alert = userInfo[@"aps"][@"alert"];
if (application.applicationState == UIApplicationStateActive) {
YHLog(@"active,接收到远程通知userInfo--");
application.applicationIconBadgeNumber = 0;
///接收到远程通知后的操作
NSString *text = [NSString stringWithFormat:@"%@%@", @"fetchCompletionHandler -- UIApplicationStateActive---", alert];
[self addTestViewRed:text];
}else if(application.applicationState == UIApplicationStateBackground){
//程序处于后台,推送时不勾选`content avaliable` 时没有这个模式
NSString *text = [NSString stringWithFormat:@"%@%@", @"fetchCompletionHandler -- UIApplicationStateBackground---", alert];
[self addTestViewRed:text];
}else if (application.applicationState == UIApplicationStateInactive){
YHLog(@"UIApplicationStateInactive");
///接收到远程通知后的操作
NSString *text = [NSString stringWithFormat:@"%@%@", @"fetchCompletionHandler -- UIApplicationStateInactive---", alert];
[self addTestViewRed:text];
}
// IOS 7 Support Required
[JPUSHService handleRemoteNotification:userInfo];
completionHandler(UIBackgroundFetchResultNewData);
}
#pragma mark JPUSHRegisterDelegate
// iOS 10 Support
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler {
// Required
NSDictionary * userInfo = notification.request.content.userInfo;
if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
[JPUSHService handleRemoteNotification:userInfo];
}
completionHandler(UNNotificationPresentationOptionAlert); // 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Alert三种类型可以选择设置
NSString *text = [NSString stringWithFormat:@"%@%@", @"JPUSHRegisterDelegate -- willPresentNotification---", userInfo[@"aps"][@"alert"]];
[self addTestViewYellow:text];
}
// iOS 10 Support
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
// Required
NSDictionary * userInfo = response.notification.request.content.userInfo;
if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
[JPUSHService handleRemoteNotification:userInfo];
}
completionHandler(); // 系统要求执行这个方法
NSString *text = [NSString stringWithFormat:@"%@%@", @"JPUSHRegisterDelegate -- didReceiveNotificationResponse---", userInfo[@"aps"][@"alert"]];
[self addTestViewYellow:text];
}
不同情况下调用的方法总结
iOS 10
1.应用在前台,
1.1 没有设置 content-available
,
- 1.1.1 调用JPUSHRegisterDelegate 方法:
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(NSInteger))completionHandler
- 1.1.2 如果此时点击了通知栏,会再调用下
JPUSHRegisterDelegate
方法:
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)())completionHandler
1.2 设置了 content-available
,
- 1.2.1 先调用方法, 其中
application.applicationState == UIApplicationStateActive
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
- 1.2.2 再调用方法
JPUSHRegisterDelegate
方法:
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(NSInteger))completionHandler
- 1.2.3 如果此时点击了通知,会再调用下 JPUSHRegisterDelegate 方法:
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)())completionHandler
2.应用在后台,
2.1 没有设置 content-available :
- 2.1.1 用户没有点击通知栏打开的app,不可以拦截.且app切换至前台后,通知栏消失.
- 2.1.2 如果用户点击了通知栏,就可以拦截.调用
JPUSHRegisterDelegate
方法:
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)())completionHandler
2.2 设置 content-available :
- 2.2.1 调用方法, 其中
application.applicationState == UIApplicationStateBackground
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
- 2.2.2 如果用户点击app图标打开的app,则当前通知栏的这条通知自动消失; 如果用户点击通知栏打开的app,会再调用下方法:
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)())completionHandler
3.应用由死到生,
3.1 无论是否设置 content-available ,
- 3.1.1 点击通知栏打开, 依次调用方法:
- 方法一
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- 方法二
JPUSHRegisterDelegate
:
- 方法二
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)())completionHandler
- 3.1.2 点击app图标打开app,则拦截不到通知.此时 launchOptions 也为空. 个人认为这正常,app从死到生,所以是正常的过程,不需要也拿不到通知信息.
一. iOS 9
应用在前台,
无论是否设置 content-available , 都会调用方法,其中 application.applicationState == UIApplicationStateActive
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
应用在后台,
没有设置 content-available , 有通知栏提示
- 2.1.1 点击通知栏会调用方法,其中
application.applicationState == UIApplicationStateInactive
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
- 2.1.2 不点击通知栏,或点击图标打开app,都不会调用上通知处理方法
2.2 设置 content-available , 有通知栏提示
- 调用方法,其中
application.applicationState == UIApplicationStateBackground
- 调用方法,其中
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
- 如果点击了通知栏, 会再次调用这个方法,其中
application.applicationState == UIApplicationStateInactive
- 如果点击了通知栏, 会再次调用这个方法,其中
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
应用由死到生,
3.1 无论是否设置 content-available
- 3.1.1 点击通知栏打开app, 依次调用方法:
- 方法一
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- 2)方法二, 其中
application.applicationState == UIApplicationStateInactive
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
- 3.1.2 点击app图标打开app,则拦截不到通知.此时 launchOptions 也为空. 个人认为这正常,app从死到生,所以是正常的过程,不需要也拿不到通知信息.