iOS本地推送通知的简单封装(定时提醒、重复提醒)

实现快捷创建简单的定时重复提醒推送功能。

主要实现原理

iOS10及以上

1.获取通知权限

iOS10及以上要先请求通知权限

UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
    //请求获取通知权限(角标,声音,弹框)
    [center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge |
                                             UNAuthorizationOptionSound |
                                             UNAuthorizationOptionAlert)
                          completionHandler:^(BOOL granted, NSError * _Nullable error) {
        if (granted) {
            //获取用户是否同意开启通知
            NSLog(@"开启通知成功!");

        }
    }];
    

2.创建通知

UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
    content.title = attribute[ZBNotificationAlertTitle];
//    content.subtitle = @"本地通知副标题";
    content.body = @"body";
    //角标数量
    content.badge = @1;
    content.userInfo = userInfo;

    //设置通知声音
    UNNotificationSound *sound = [UNNotificationSound defaultSound];
    content.sound = sound;
    

创建 UNNotificationRequest :

//设置时间容器:传人date中所有时间元素放入时间容器
NSDateComponents * components = [[NSCalendar currentCalendar]
                                     components:NSCalendarUnitYear |
                                     NSCalendarUnitMonth |
                                     NSCalendarUnitWeekday |
                                     NSCalendarUnitDay |
                                     NSCalendarUnitHour |
                                     NSCalendarUnitMinute |
                                     NSCalendarUnitSecond
                                     fromDate:date];
//设置UNCalendarNotificationTrigger
//repeats: 设置是否重复
UNCalendarNotificationTrigger * trigger = [UNCalendarNotificationTrigger 
                                            triggerWithDateMatchingComponents:components 
                                            repeats:repeat];
//设置UNNotificationRequest
//identifer:设置通知标识符或者说通知名字
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifer content:content trigger:trigger];

3.添加通知

//把通知加到UNUserNotificationCenter, 到指定触发点会被触发
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
    if (error) {
        NSLog(@"通知添加失败:%@",error);
    } else {
        NSLog(@"通知添加成功");
    }
}];

4.重复提醒

如果repeats为YES时为重复提醒
约定在特定时间提醒时我们用时间容器来实现

NSDateComponents * components = [[NSCalendar currentCalendar]
                                     components:NSCalendarUnitYear |
                                     NSCalendarUnitMonth |
                                     NSCalendarUnitWeekday |
                                     NSCalendarUnitDay |
                                     NSCalendarUnitHour |
                                     NSCalendarUnitMinute |
                                     NSCalendarUnitSecond
                                     fromDate:date];
                                     

比如设置一个每天都提醒的推送,传入的时间设置为:

NSDateFormatter * formatter = [[NSDateFormatter alloc]init];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSDate * date = [formatter dateFromString:@"2019-01-01 08:00:00"];

每天8点 进行推送提醒的 时间容器 为:

NSDateComponents * components = [[NSCalendar currentCalendar]
                                     components:NSCalendarUnitHour |
                                                NSCalendarUnitMinute |
                                                NSCalendarUnitSecond
                                     fromDate:date];
                                     

每周二8点 (19年1月1号为周二) 进行推送提醒的 时间容器 为:

NSDateComponents * components = [[NSCalendar currentCalendar]
                                     components:NSCalendarUnitWeekday |
                                     NSCalendarUnitHour |
                                     NSCalendarUnitMinute |
                                     NSCalendarUnitSecond
                                     fromDate:date];
                                     

每月1号8点 进行推送提醒的 时间容器 为:

NSDateComponents * components = [[NSCalendar currentCalendar]
                                     components:NSCalendarUnitDay |
                                     NSCalendarUnitHour |
                                     NSCalendarUnitMinute |
                                     NSCalendarUnitSecond
                                     fromDate:date];
                                     

每年1月1号8点 进行推送提醒的 时间容器 为:

NSDateComponents * components = [[NSCalendar currentCalendar]
                                     components:NSCalendarUnitMonth |
                                     NSCalendarUnitDay |
                                     NSCalendarUnitHour |
                                     NSCalendarUnitMinute |
                                     NSCalendarUnitSecond
                                     fromDate:date];
                                     

以此类推..
如果有需求是要 周一到周五的每天8点 提醒时,我的做法是用for循环创建5个通知:

//因为周日是一星期第一天,1代表周日,所以周一从2开始
for (NSInteger i = 2; i <= 6; i++) {
//这里时间容器创建和以上每周提醒的一样,省略。
        components.weekday = i;
        
//然后用这个components去添加通知就可以实现重复通知了
}

5.取消通知

//找到要取消的通知名字
NSMutableArray * names = [[NSMutableArray alloc]initWithObjects:notificationName, nil];
//批量取消这些通知
[[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:names];

iOS10以下

1.创建通知

UILocalNotification *localNotification = [[UILocalNotification alloc]init];
    
    // 设置触发时间
    localNotification.fireDate = date;
    // 设置时区  以当前手机运行的时区为准
    localNotification.timeZone = [NSTimeZone localTimeZone];
    // 设置推送 显示的内容
    localNotification.alertTitle = @"title";
    localNotification.alertBody = @"body";
    // 设置 角标
    localNotification.applicationIconBadgeNumber = 1;
    // 不设置此属性,则默认不重复
    localNotification.repeatInterval =  repeatInterval;
    
    // 设置推送的声音
    localNotification.soundName =  UILocalNotificationDefaultSoundName;
    
    
    localNotification.userInfo = userInfo;
    //添加到通知
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
    

2.重复提醒

重复提醒通过设置 repeatInterval 就可以

value effect
NSCalendarUnitDay 每天
NSCalendarUnitWeekday 每周
NSCalendarUnitMonth 每月
NSCalendarUnitYear 每年
... ...

等等...
同样如果有需求是周一到周五某时提醒也用循环创建五个通知

for (NSInteger i = 2; i <= 6; i++) {
//同样用NSDateComponents设置weekday来实现星期几的提醒
//先用传入的date创建时间容器,然后修改weekday后再转为NSDate
    NSDateComponents * components = [[NSCalendar currentCalendar] components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:date];
    components.weekday = i;
    NSDate * newDate = [[NSCalendar currentCalendar] dateFromComponents:components];
//吧newDate再赋值给fireDate
}

3.取消通知

//获取所有通知
NSArray * localNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
    for (UILocalNotification * notification in localNotifications) {
        //找到和要删除的通知同名的通知删除
        if ([notification.userInfo[@"notificationName"] hasPrefix:notificationName]) {
            //删除通知
            [[UIApplication sharedApplication] cancelLocalNotification:notification];

        }
    }
    

注册通知

//注册通知
[[UIApplication sharedApplication] registerForRemoteNotifications];
    

快捷使用

封装了一个可以直接使用的类:ZBLocalNotification

//提醒重复类型
typedef NS_ENUM(NSInteger, ZBLocalNotificationRepeat) {
    ZBLocalNotificationRepeatNone,          //不重复
    ZBLocalNotificationRepeatEveryDay,      //每天
    ZBLocalNotificationRepeatEveryWeek,     //每周
    ZBLocalNotificationRepeatEveryMonth,    //每月
    ZBLocalNotificationRepeatEveryYear,     //每年
    ZBLocalNotificationRepeatEveryWorkDay   //每周一到周五(工作日)
};

//标识通知属性的key
typedef NSString * ZBLocalNotificationKey;
//标识通知声音文件名字的key
typedef NSString * ZBLocalNotificationSoundName;

extern ZBLocalNotificationKey const ZBNotificationFireDate;     //标识提醒时间
extern ZBLocalNotificationKey const ZBNotificationAlertTitle;   //标识标题
extern ZBLocalNotificationKey const ZBNotificationAlertBody;    //标识提醒内容
extern ZBLocalNotificationKey const ZBNotificationAlertAction;  //标识按钮
extern ZBLocalNotificationKey const ZBNotificationSoundName;    //标识声音
extern ZBLocalNotificationKey const ZBNotificationUserInfoName; //标识通知名字
extern ZBLocalNotificationKey const ZBNotificationPriority;     //标识通知优先级
extern ZBLocalNotificationKey const ZBNotificationRepeat;       //标识通知重复
extern ZBLocalNotificationSoundName const ZBNotificationSoundAlarm; //标识声音为提醒
extern ZBLocalNotificationSoundName const ZBNotificationSoundOther; //标识声音为其他

@interface ZBLocalNotification : NSObject

/**
 创建本地通知

 @param attribute 通知的属性
 */
+ (void)createLocalNotificationWithAttribute:(NSDictionary *)attribute;

/**
 取消通知

 @param notificationName 通知名字
 */
+ (void)cancelLocalNotificationWithName:(NSString *)notificationName;

#ifdef NSFoundationVersionNumber_iOS_9_x_Max

/**
 注册通知
 */
+ (void)requestUNUserNotificationAuthorization;

#endif

@end

创建时间为date的每天重复提醒例子:
导入#import "ZBLocalNotification.h"

[ZBLocalNotification createLocalNotificationWithAttribute:
                                @{ZBNotificationUserInfoName:@"notificationName",
                                  ZBNotificationSoundName:ZBNotificationSoundAlarm,
                                  ZBNotificationAlertBody:@"提醒内容",
                                  ZBNotificationAlertTitle:@"提醒标题",
                                  ZBNotificationFireDate:date,
                                  ZBNotificationPriority:@(0),
                                  ZBNotificationRepeat:@(ZBLocalNotificationRepeatEveryDay)
                                  }];
                                  

取消名字为 “notificationName” 的通知:

[ZBLocalNotification cancelLocalNotificationWithName:@"notificationName"];

通知优先级问题

如果设置了多个推送通知,并且时间都在同一个时刻时,就是同时收到多个推送通知时,想要只显示优先级最高的一个,我是在AppDelegate里这样处理的(希望有更好的方法能不吝赐教 :) ):
先创建一个userInfo容器,保存同一时间收到的通知
self.userInfos = [[NSMutableArray alloc]init];

//处理接收到的通知信息
- (void)filteredUserInfo {
    if (self.userInfos.count == 0) {
        return;
    }
    //选出你希望显示的通知信息,以下方法是显示优先级高的,你可以判断不同的条件
    //排序所有收到的通知信息,对比优先级,把优先级最高的放首位
    [self.userInfos sortUsingComparator:^NSComparisonResult(NSDictionary * obj1, NSDictionary * obj2) {
        if ([obj1[ZBNotificationPriority] integerValue] < [obj2[ZBNotificationPriority] integerValue]) {
            return NSOrderedDescending;
        }
        return NSOrderedSame;
    }];
    
    //自定义展示推送内容
    [self showAlarmAlertWithUserInfo:self.userInfos.firstObject];
    //重置userInfo容器
    [self.userInfos removeAllObjects];
}
//添加通知到userInfo容器
- (void)waitMultipleUserInfo:(NSDictionary *)userInfo {
    [self.userInfos addObject:userInfo];
    //创建信号量,设置为0
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(quene, ^{
        
        if (self.userInfos.count == 1) {
            //信号量为0时,那么这个函数就阻塞当前线程等待timeout,时间到后继续执行
            //0.3秒内第一次进入则等待0.3秒,0.3秒后对本时间段内提醒提取优先级最高的一个
            //就是保存在极短时间内(我这里设置为0.3s)收到的所有通知,然后进行处理
            dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC));
            dispatch_async(dispatch_get_main_queue(), ^{
                [self filteredUserInfo];
            });
        }
    });
    
}

#pragma mark - localNotification
// iOS10以下 在前台收到推送回调
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
    [self waitMultipleUserInfo:notification.userInfo];
}

#ifdef NSFoundationVersionNumber_iOS_9_x_Max

#pragma mark - UNUserNotificationCenterDelegate
// iOS10 在前台收到推送回调
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(nonnull UNNotification *)notification withCompletionHandler:(nonnull void (^)(UNNotificationPresentationOptions))completionHandler{
    UNNotificationRequest *request = notification.request; // 收到推送的请求
    UNNotificationContent *content = request.content; //收到推送消息的全部内容
    
    if ([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        NSLog(@"iOS10 收到远程通知");
    }else{
        NSLog(@"ios10 收到本地通知userInfo:%@",content.userInfo);
        [self waitMultipleUserInfo:content.userInfo];
    }
    completionHandler(UNNotificationPresentationOptionBadge |
                      UNNotificationPresentationOptionSound
                       );
    
}

#endif

代码下载

ZBLocalNotification类下载地址:
iOS本地推送封装(定时推送、重复提醒)
ZBLocalNotification类下载地址:
iOS本地推送封装(定时推送、重复提醒)

疑问

比如需求是可以自定义重复的时间:

  • 每隔两周的周二早上8点提醒
  • 每隔三个月的7号早上8点提醒

这种时候的怎么实现,NSDateComponents需要怎么设置?

谢谢!

欢迎讨论,未完待续


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容