iOS 知识点iOS10以后的新技术程序员

iOS10本地推送,上线项目中的实际运用

2017-03-26  本文已影响1658人  _zhouxl

离上次写iOS集成极光推送之后,我还得这样做总结已过去半年了,半年的时间,在码代码的过程中,慢慢的体会到了一点,看到需求、设计不要急于写代码,先在脑子里想想,该怎么设计,应该考虑哪些方面的问题,怎么编写效率更高,用户体验更好等等?这样才能更快的提升。

有关iOS10推送的介绍和使用,早有很多前辈已写好了博客,这里我就不介绍怎么使用推送了,着重介绍本地推送在实际项目中的使用流程,以及需要注意的地方。苹果提供本地推送这么一个功能,我想一般是用来结合网络、时间针对性的提醒某些用户的。

相比远程推送,1.本地推送没有相关证书方面的要求;2.在没有自己平台推送服务器的情况下,可以在app内部对用户进行推送提醒。而远程推送最大的优点就是可以轻松利用三方平台,无论用户是否有网络,都能随时对app用户进行推送,当然也可以对个别用户进行推送。关键还得看需求~


应用处于前台收到通知

谈谈最近公司接的一个项目中的一个需求,以下是项目需求:

用户在使用APP的过程中,要能及时(30s)看到他的交易信息,
1. 在APP内以弹窗的形式提醒用户,点击“查看”跳转到对应消息界面;
2. 在后台运行时,以推送通知的形式提醒用户,用户点击消息跳转到对应的消息界面。
由于没有自己的推送服务器,又涉及到网络请求,暂不考虑APP处于退出状态时的情况。

看到这么一个需求,当时就想到用推送,至于远程推还是本地推,当然选择后者,远程推的话必须借助极光推送平台,而且不能满足从公司后台获取用户实时交易的消息,并有针对性的给用户推送。

下面说说实现思路,涉及到iOS10推送API读取用户对通知权限的设置定时器通知中心界面跳转定时器和通知的移除,以下关于本地通知都用iOS10的API。

1.给APP申请通知权限,
2.读取用户对通知的设置;
3.创建定时器;
4.添加通知中心观察者;
5.创建 UNUserNotificationCenter对象,配置推送内容;
6.分别处理前台和后台接收通知的情况;
7.用户点击通知后的界面跳转;
8.移除定时器、通知中心对象。

开始上代码:

    // 1.给APP申请通知权限,关于请求权限(定位等)务必放在APPdelegate中,程序一启动马上提醒用户选择.否则设置-通知中心根本就没有此应用程序的通知设置,自己想去后台设置打开都找不到地方。
    // 这是iOS10请求通知权限的API
    [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) {
        // 用户对通知的设置
        [[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
            DLog(@"%@", (long)settings.authorizationStatus == 2 ? @"成获取到通知权限" : @"用户关闭了通知");
            if (settings.authorizationStatus != 2) {
                DLog(@"用户关闭了通知");
            }
        }];
    }];
// 2.检查用户对通知功能的设置状态
- (BOOL)isPermissionedPushNotification {
    if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) {
        DLog(@"用户关闭了通知功能");
        // 弹窗
        UIAlertController *alertvc = [UIAlertController alertControllerWithTitle:@"温馨提醒" message:@"请打开通知功能,否则无法收到交易提醒." preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"去设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            DLog(@"引导用户去设置中心打开通知");
            // 点击跳转至设置中心
            NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
            if([[UIApplication sharedApplication] canOpenURL:url]) {
                NSURL *url =[NSURL URLWithString:UIApplicationOpenSettingsURLString];
                [[UIApplication sharedApplication] openURL:url];
            }
        }];
        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"忽略交易提醒" style:UIAlertActionStyleCancel handler:nil];
        
        [alertvc addAction:cancelAction];
        [alertvc addAction:confirmAction];
        [self presentViewController:alertvc animated:YES completion:nil];
        
        return false;
    } else {
        DLog(@"可以进行推送");
        return true;
    }
}
- (void)viewDidLoad {
    [super viewDidLoad];
    // ...
    if ([self isPermissionedPushNotification])
    {
        // 3.创建定时器,获得新消息未读数
        self.timer = [NSTimer scheduledTimerWithTimeInterval:RequestTimeInterval target:self selector:@selector(setupUnreadTradeMsg) userInfo:nil repeats:YES];
        // 设置timer模式
        [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
    }

    // 4.创建通知中心观察者
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(confirmReadNoticeContent:) name:@"ReadNoticeContent" object:nil];
}

// 定时器执行方法
- (void)setupUnreadTradeMsg {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [NetworkEngine signPostWithURL:PARAMQUERY parmas:params success:^(id requestResult) {
                // ...
                // 省略部分网络请求代码
                
                // 设置app角标数量 = 未读消息数量
                [[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge];
                // 消息按钮红点
                [self.msgBtn setBackgroundImage:[UIImage imageNamed:@"new_message"] forState:UIControlStateNormal];
                // 将新的交易信息内容传入推送
                NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
                // ...
                // 发起推送
                [self pushNotificationOfTradeMsg:params];
            } failure:^(NSError *error) {
                // [self showToast:@"请求失败,请检查网络连接"];
                return ;
            }];
        });
// 5.配置推送内容
- (void)pushNotificationOfTradeMsg:(NSDictionary *)params {
    // 1、创建通知对象
    UNUserNotificationCenter *center  = [UNUserNotificationCenter currentNotificationCenter];
    // 必须设置代理,否则无法收到通知
    center.delegate = self;
    // 权限
    [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) {
        if (granted) {
            // 2、创建通知内容
            UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
            // 标题
            content.title = params[@"title"];
            content.body = params[@"content"];
            // 声音
            content.sound = [UNNotificationSound soundNamed:@"notification.wav"];
            // 图片
            NSURL *imageUrl = [[NSBundle mainBundle] URLForResource:@"msgImg" withExtension:@"png"];
            UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:@"imageIndetifier" URL:imageUrl options:nil error:nil];
            content.attachments = @[attachment];
            // 标识符
            content.categoryIdentifier = @"categoryIndentifier";
            
            // 3、创建通知触发时间
            UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:1.0 repeats:NO];
            // 4、创建通知请求
            UNNotificationRequest *notificationRequest = [UNNotificationRequest requestWithIdentifier:@"KFGroupNotification" content:content trigger:trigger];
            // 5、将请求加入通知中心
            [center addNotificationRequest:notificationRequest withCompletionHandler:^(NSError * _Nullable error) {
                if (!error) {
                    DLog(@"已成功加入通知请求");
                } else {
                    DLog(@"出错啦%@", [error localizedDescription]);
                }
            }];
        }
    }];
}
/** 6.1当用户处于前台时,消息发送前走这个方法 */
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{
    DLog(@"--------通知即将发出-------");
    // 在前台时,通过此方法监听到消息发出,不让其在通知栏显示,以弹窗的形式展示出来;设置声音提示
    completionHandler(UNNotificationPresentationOptionSound);
    
    // 获取消息内容
    NSMutableDictionary *content = [[NSMutableDictionary alloc] init];
    [content setObject:notification.request.content.title forKey:@"content"];
    [content setObject:notification.request.content.body forKey:@"body"];
    
    // 弹窗
    UIAlertController *alertvc = [UIAlertController alertControllerWithTitle:notification.request.content.title message:notification.request.content.body preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction* updateAction = [UIAlertAction actionWithTitle:@"查看" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        // 创建通知对象
        NSNotification *notice = [NSNotification notificationWithName:@"ReadNoticeContent" object:nil userInfo:content];
        // 发送通知
        [[NSNotificationCenter defaultCenter] postNotification:notice];
        DLog(@"点击查看消息");
    }];
    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"忽略" style:UIAlertActionStyleCancel handler:nil];
    [alertvc addAction:cancelAction];
    [alertvc addAction:updateAction];
    [self presentViewController:alertvc animated:YES completion:nil];
}
/** 6.2不处于前台时,与通知交互走这个方法 */
-(void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {

    completionHandler(UIBackgroundFetchResultNewData);
    // 获取消息内容
    NSMutableDictionary *content = [[NSMutableDictionary alloc] init];
    [content setObject:response.notification.request.content.title forKey:@"content"];
    [content setObject:response.notification.request.content.body forKey:@"body"];
    
    // 判断是否为本地通知 
    if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        NSLog(@"收到远程通知");
    } else {
        // 判断为本地通知
        //创建通知对象
        NSNotification *notice = [NSNotification notificationWithName:@"ReadNoticeContent" object:nil userInfo:content];
        //发送通知
        [[NSNotificationCenter defaultCenter] postNotification:notice];
    }
}
// 点击查看消息,进行界面跳转
- (void)confirmReadNoticeContent:(NSDictionary *)content {
    DLog(@"跳转页面 %@", content);
    
    // 点击通知查看内容,角标清零,红点消失
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
    [self.msgBtn setBackgroundImage:[UIImage imageNamed:@"nav_message"] forState:UIControlStateNormal];
    
    // 获取当前跟控制器
    UIViewController *rootVC = nil;
    UIWindow *window = [UIApplication sharedApplication].windows.firstObject;
    if ([window.rootViewController isKindOfClass:[GFNavigationController class]])
    {
        rootVC = [(GFNavigationController *)window.rootViewController visibleViewController];
    }
    else if ([window.rootViewController isKindOfClass:[GFTabBarController class]])
    {
        GFTabBarController *tabVC = (GFTabBarController *)window.rootViewController;
        rootVC = [(GFNavigationController *)[tabVC selectedViewController] visibleViewController];
    }
    else
    {
        rootVC = window.rootViewController;
    }
    
    // 跳转到消息列表页面
    TradeMessageViewController *tmvc = [[TradeMessageViewController alloc] init];
    [rootVC.navigationController pushViewController:tmvc animated:YES];
}
- (void)dealloc {
    // 移除通知
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    // 销毁定时器
    [self.timer invalidate];
    self.timer = nil;
}
处于前台时收到消息.gif 处于后台时收到消息.gif

至此,消息已经可以成功发出,从前台点击查看或从后台点击都可以成功进行界面跳转,然后返回到之前的页面。
总结:

1、UNUserNotificationCenter对象的创建及内容配置可以根据需要放在控制器中,不一定非得放在APPdelegate中;
2、上面前台和后台点击查看消息内容是以发通知的形式进行处理的,其实可以直接用[self methodName]的形式调用,原因是接收通知的方法和点击查看消息的方法在同一个控制器中,如果不在同一个控制器中(跨界面)就得用通知进行接收了。
3、这里我的跟控制器是TabBar,所以界面跳转走的是第二个else if,如果是导航控制器或其他,会跟着变的。

最后,由于是客户的项目,就没有单独整理demo。本地通知实际运用并不难,自己稍微花点时间整理下,就全部弄明白了,思路流程都已经列出来了,可以根据需要一步一步来,这个弄清楚,其实远程通知也差不多了。好了,如果发现有不对或者需要优化的地方,请指出,真心感谢!

上一篇下一篇

猜你喜欢

热点阅读