// // AppDelegate+AliPushConfig.m // Ifish // // Created by wbzhan on 2019/5/23. // Copyright © 2019 lianlian. All rights reserved. // #import "AppDelegate+AliPushConfig.h" #import #import "IfishMessageViewController.h" static const void *NotificationCenterKey = &NotificationCenterKey; @implementation AppDelegate (AliPushConfig) - (UNUserNotificationCenter *)notificationCenter { return objc_getAssociatedObject(self, NotificationCenterKey); } - (void)setNotificationCenter:(UNUserNotificationCenter *)notificationCenter { objc_setAssociatedObject(self, NotificationCenterKey, notificationCenter, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } /** * 向APNs注册,获取deviceToken 用于推送 * @param application */ - (void)registerAPNS:(UIApplication *)application { float systemVersionNum = [[[UIDevice currentDevice] systemVersion] floatValue]; if (systemVersionNum >= 10.0) { // iOS 10 notifications self.notificationCenter = [UNUserNotificationCenter currentNotificationCenter]; // 创建 category,并注册到通知中心 [self createCustomNotificationCategory]; // 遵循协议 self.notificationCenter.delegate = self; // 请求客户推送通知权限,以及推送的类型 [self.notificationCenter requestAuthorizationWithOptions:UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) { if (granted) { // granted NSLog(@"\n ====== User authored notification."); // 向APNs注册,获取deviceToken // 要求在主线程中 dispatch_async(dispatch_get_main_queue(), ^{ [application registerForRemoteNotifications]; }); } else { // not granted NSLog(@"\n ====== User denied notification."); // 即使客户不允许通知也想让它通知 // 待测试 // 向APNs注册,获取deviceToken // 要求在主线程中 dispatch_async(dispatch_get_main_queue(), ^{ [application registerForRemoteNotifications]; }); }}]; /** * 主动获取设备通知是否授权 (iOS 10+) */ [self.notificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { // 进行判断做出相应的处理 if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) { NSLog(@"\n ====== 未选择是否允许通知"); } else if (settings.authorizationStatus == UNAuthorizationStatusDenied) { NSLog(@"\n ====== 未授权允许通知"); } else if (settings.authorizationStatus == UNAuthorizationStatusAuthorized){ NSLog(@"\n ====== 已授权允许通知"); } }]; } else if (systemVersionNum >= 8.0) { // 适配 iOS_8, iOS_10.0 // iOS 8 Notifications // 不会有黄色叹号 #pragma clang diagnostic push #pragma clang diagnostic ignored"-Wdeprecated-declarations" [application registerUserNotificationSettings: [UIUserNotificationSettings settingsForTypes: (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]]; [application registerForRemoteNotifications]; /* // 提出弹窗,授权是否允许通知 UIUserNotificationSettings * settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert categories:nil]; [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; // 注册远程通知 (iOS_8+) [[UIApplication sharedApplication] registerForRemoteNotifications]; if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) { //判断用户是否打开通知开关 NSLog(@"没有打开"); } else { NSLog(@"已经打开"); } */ #pragma clang diagnostic pop } else { // iOS < 8 Notifications #pragma clang diagnostic push #pragma clang diagnostic ignored"-Wdeprecated-declarations" [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)]; /* UIRemoteNotificationType types = UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeSound|UIRemoteNotificationTypeAlert; [[UIApplication sharedApplication] registerForRemoteNotificationTypes:types]; if ([[UIApplication sharedApplication] enabledRemoteNotificationTypes] == UIRemoteNotificationTypeNone) { //判断用户是否打开通知开关 } */ #pragma clang diagnostic pop } } /** * 主动获取设备通知是否授权 (iOS 10+) 可以单独调用 */ - (void)getNotificationSettingStatus { [self.notificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { if (settings.authorizationStatus == UNAuthorizationStatusAuthorized) { NSLog(@"\n ====== 已经开启通知."); } else { NSLog(@"\n ====== 暂未开启通知."); }}]; } /** * 创建并注册通知category (iOS 10+) */ - (void)createCustomNotificationCategory { // 自定义 action1 和 action2 UNNotificationAction *action1 = [UNNotificationAction actionWithIdentifier:@"action1" title:@"test1" options: UNNotificationActionOptionNone]; UNNotificationAction *action2 = [UNNotificationAction actionWithIdentifier:@"action2" title:@"test2" options: UNNotificationActionOptionNone]; // 创建id为`test_category`的category,并注册两个action到category // UNNotificationCategoryOptionCustomDismissAction 表明可以触发通知的 dismiss 回调 UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:@"test_category" actions:@[action1, action2] intentIdentifiers:@[] options: UNNotificationCategoryOptionCustomDismissAction]; // 注册category到通知中心 [self.notificationCenter setNotificationCategories:[NSSet setWithObjects:category, nil]]; } /* * APNs注册成功回调,将返回的deviceToken上传到CloudPush服务器 */ - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken NS_AVAILABLE_IOS(3_0) { NSLog(@"Upload deviceToken to CloudPush server."); //阿里推送注册成功回调 [CloudPushSDK registerDevice:deviceToken withCallback:^(CloudPushCallbackResult *res) { if (res.success) { NSString *userId = [dataContorl dataControlGetUserIdInfo]; NSString *device_Id = [CloudPushSDK getDeviceId]; NSLog(@"注册成功 device_token = %@",device_Id); [FuncUserDefault setStr:device_Id key:kDeviceToken];//本机的devicetoken保存在本地 if (userId!=nil) { NSDictionary *params = @{@"deviceId":device_Id, @"userId":userId, @"phoneType":@"iOS" }; [AFNOHeaderHttpTool requestWihtMethod:RequestTypePost url:Push_BindDevice_Url params:params success:^(id response) { id resault=[NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingMutableContainers error:nil]; NSLog(@"result = %@",resault); //绑定成功 [FuncUserDefault setBool:YES key:is_Bind_DeviceToken];//已经上送了本机的devicetoken } failure:^(NSError *err) { }]; } } else { NSLog(@"Register deviceToken failed, error: %@", res.error); } }]; } /* * APNs注册失败回调 */ - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { NSLog(@"\n ====== didFailToRegisterForRemoteNotificationsWithError %@", error); } #pragma mark SDK Init - (void)registerAliPush { // // 正式上线关闭 // if (DEBUG) { // [CloudPushSDK turnOnDebug]; // } // SDK初始化 [CloudPushSDK asyncInit:Ali_Push_APPKey appSecret:Ali_Push_APPSecret callback:^(CloudPushCallbackResult *res) { if (res.success) { NSLog(@"\n ====== 启动成功: %@.", [CloudPushSDK getDeviceId]); } else { NSLog(@"\n ====== 启动失败, error: %@", res.error); } }]; // 监听推送通道打开动作 [self listenerOnChannelOpened]; // 监听推送消息到达 [self registerMessageReceive]; // 主动获取设备通知是否授权 (iOS 10+) [self getNotificationSettingStatus]; } /** * 注册推送通道 打开 监听 */ - (void)listenerOnChannelOpened { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onChannelOpened:) name:@"CCPDidChannelConnectedSuccess" object:nil]; } /** * 推送通道打开回调 * @param notification */ - (void)onChannelOpened:(NSNotification *)notification { NSLog(@"\n ====== 温馨提示,消息通道建立成功,该通道创建成功表示‘在线’,可以接收到推送的消息"); } /** * 注册推送消息 到来 监听 */ - (void)registerMessageReceive { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMessageReceived:) name:@"CCPDidReceiveMessageNotification" object:nil]; } /** * 处理到来推送消息 * @param notification */ - (void)onMessageReceived:(NSNotification *)notification { //消息在通知栏显示,属于透传内容 NSLog(@"\n ====== Receive one message !!!!!!!"); CCPSysMessage *message = [notification object]; NSString *title = [[NSString alloc] initWithData:message.title encoding:NSUTF8StringEncoding]; NSString *body = [[NSString alloc] initWithData:message.body encoding:NSUTF8StringEncoding]; NSLog(@"\n ====== Receive message title: %@, content: %@.", title, body); NSLog(@"\n ====== 当前线程 %@",[NSThread currentThread]); // [CommonUtils showAlerWithTitle:IsEmptyStr(title)?@"新消息":title withMsg:body]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:IsEmptyStr(title)?@"新消息":title message:body preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *alertAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { //进入消息列表页面 IfishMessageViewController *message = [[IfishMessageViewController alloc]init]; UIViewController *vc= (UINavigationController *)[CommonUtils appRootViewController]; vc.hidesBottomBarWhenPushed = YES; [vc.navigationController pushViewController:message animated:YES]; vc.hidesBottomBarWhenPushed = NO; }]; [alertController addAction:alertAction]; [[CommonUtils appRootViewController] presentViewController:alertController animated:YES completion:nil]; } #pragma mark —页面跳转(暂时不做处理) - (void)jumpViewController:(NSDictionary *)tfdic { // NSDictionary *remoteNotification = [tfdic objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; // for (NSString *tfStr in remoteNotification) { // 通知里面的内容需要包含 " " // if ([tfStr isEqualToString:@"careline"]) { // JumpViewController *_viewController = [[JumpViewController alloc]init]; // UINavigationController *nav= (UINavigationController *)self.window.rootViewController; // [nav pushViewController:_viewController animated:YES]; // } // } } /** * iOS 10 + 实现两个代理方法之一。 * 当APP处于后台 点击通知栏通知 * 触发通知动作时回调,比如点击、删除通知和点击自定义 action(iOS 10+) */ - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { NSString *userAction = response.actionIdentifier; // 点击通知打开app(app处于后台) if ([userAction isEqualToString:UNNotificationDefaultActionIdentifier]) { NSLog(@"\n ====== User opened the notification."); // 处理iOS 10通知,并上报通知打开回执 [self handleiOS10Notification:response.notification isActive:NO]; } // 通知dismiss,category 创建时传入 UNNotificationCategoryOptionCustomDismissAction 才可以触发 if ([userAction isEqualToString:UNNotificationDismissActionIdentifier]) { NSLog(@"\n ====== User dismissed the notification."); } completionHandler(); } /** * App 处于前台时收到通知 (iOS 10+ ) * iOS 10 + 实现两个代理方法之一。 * 只有当应用程序位于前台时,该方法才会在委托上调用。如果方法未被执行或处理程序没有及时调用,则通知将不会被提交。 * 应用程序可以选择将通知呈现为声音、徽章、警报和/或通知列表中。此决定应基于通知中的信息是否对用户可见。 */ - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { NSLog(@"\n ====== App 处于前台时收到通知 (iOS 10+ )"); // 处理iOS 10通知,并上报通知打开回执 [self handleiOS10Notification:notification isActive:YES]; /* 处理完成后调用 completionHandler ,用于指示在前台显示通知的形式 completionHandler() 功能:可设置是否在应用内弹出通知 在 iOS 10 中 通知在前台的显示设置: */ // 1、通知在前台不显示 // 如果调用下面代码: 通知不在前台弹出也不在通知栏显示 // completionHandler(UNNotificationPresentationOptionNone); // 2、通知在前台显示 // 如果调用下面代码: 通知在前台弹出也在通知栏显示 // completionHandler(UNNotificationPresentationOptionAlert); // 3、通知在前台显示 并带有声音 // 如果调用下面代码:通知弹出,且带有声音、内容和角标 // completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge); completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionBadge); } /** * 处理App 处于前台时收到通知 (iOS 10+ ) */ - (void)handleiOS10Notification:(UNNotification *)notification isActive:(BOOL)isActive{ UNNotificationRequest *request = notification.request; UNNotificationContent *content = request.content; NSDictionary *userInfo = content.userInfo; // 通知时间 NSDate *noticeDate = notification.date; // 标题 NSString *title = content.title; // 副标题 NSString *subtitle = content.subtitle; // 内容 NSString *body = content.body; // 角标 int badge = [content.badge intValue]; // 取得通知自定义字段内容,例:获取key为"category"的内容 NSString *extras = [userInfo valueForKey:@"category"]; // 通知角标数清0 [UIApplication sharedApplication].applicationIconBadgeNumber = 0; // 同步角标数到服务端 [self syncBadgeNum:0]; // 通知打开回执上报 [CloudPushSDK sendNotificationAck:userInfo]; //App处于前台 if (isActive) { NSLog(@"\n ====== App 处于前台时收到通知 (iOS 10+ ) Notification, == date: %@, == title: %@, == subtitle: %@, == body: %@, == badge: %d, == extras: %@.", noticeDate, title, subtitle, body, badge, extras); UIAlertController *alertController = [UIAlertController alertControllerWithTitle:IsEmptyStr(title)?@"新通知":title message:body preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *alertAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { //进入消息列表页面 IfishMessageViewController *message = [[IfishMessageViewController alloc]init]; UIViewController *vc= (UINavigationController *)[CommonUtils appRootViewController]; vc.hidesBottomBarWhenPushed = YES; [vc.navigationController pushViewController:message animated:YES]; vc.hidesBottomBarWhenPushed = NO; }]; [alertController addAction:alertAction]; [[CommonUtils appRootViewController] presentViewController:alertController animated:YES completion:nil]; }else{ NSLog(@"\n ====== App 处于后台时收到通知 (iOS 10+ ) Notification,点击通知打开应用, == date: %@, == title: %@, == subtitle: %@, == body: %@, == badge: %d, == extras: %@.", noticeDate, title, subtitle, body, badge, extras); //进入消息列表页面 IfishMessageViewController *message = [[IfishMessageViewController alloc]init]; UIViewController *vc= (UINavigationController *)[CommonUtils appRootViewController]; vc.hidesBottomBarWhenPushed = YES; [vc.navigationController pushViewController:message animated:YES]; vc.hidesBottomBarWhenPushed = NO; } } /* 同步通知角标数到服务端 */ - (void)syncBadgeNum:(NSUInteger)badgeNum { [CloudPushSDK syncBadgeNum:badgeNum withCallback:^(CloudPushCallbackResult *res) { if (res.success) { NSLog(@"\n ====== Sync badge num: [%lu] success.", (unsigned long)badgeNum); } else { NSLog(@"\n ====== Sync badge num: [%lu] failed, error: %@", (unsigned long)badgeNum, res.error); } }]; } // iOS 7+ 不论是前台还是后台只要有远程推送都会调用 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler NS_AVAILABLE_IOS(7_0) { NSLog(@"\n ====== iOS 7+ 前台后台都会调用"); /* 建议使用该方法,还有一个作用。根据苹果给出的文档,系统给出30s的时间对推送的消息进行处理,此后就会运行CompletionHandler 程序块。 在处理这类推送消息(即程序被启动后接收到推送消息)的时候,通常会遇到这样的问题 : 就是当前的推送消息是当前程序正在前台运行时接收到的,还是说是程序在后台运行,用户点击系统消息通知栏对应项进入程序时而接收到的?这个其实很简单,用下面的代码就可以解决: */ // 做相应的判断是前台还是后台 if (application.applicationState == UIApplicationStateActive) { // 程序当前正处于前台 如果不做处理里面不用写 就可以了 /* 关于userInfo的结构,参照苹果的官方结构: { "aps" : { "alert" : "You got your emails.", "badge" : 9, "sound" : "bingbong.aiff" "acme1" : "bar", "acme2" : 42 } 即key aps 对应了有一个字典,里面是该次推送消息的具体信息。具体跟我们注册的推送类型有关。另外剩下的一些key就是用户自定义的了。 */ } else if (application.applicationState == UIApplicationStateInactive) { // //点击通知打开app,则跳转到消息列表 // IfishMessageViewController *message = [[IfishMessageViewController alloc]init]; // UIViewController *vc= (UINavigationController *)[CommonUtils appRootViewController]; // vc.hidesBottomBarWhenPushed = YES; // [vc.navigationController pushViewController:message animated:YES]; // vc.hidesBottomBarWhenPushed = NO; } } @end