本地推送通知

iOS常用通知

1、本地推送通知:(Local Notification):
  不需要联网就可以发出的通知, 常用于提醒用户完成一些任务,如:闹钟,日历待办事项提醒,备注等等;
2、远程推送通知:(Remote Notification)
  从远程服务器(APNs)推送给客户端的通知(需要联网), 模拟器无法调试远程推送;
  常用于解决获取传统数据的局限性,能让数据实时更新, 在联网的情况下, 苹果设备都会与APNs建立长连接, 因此能保证数据传输速度快及保证数据的实时性...

本篇只介绍本地推送通知:

通知的授权与注册: (分iOS10.0 以后 与 之前)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 通知的授权检查
    [self checkNotificationAuthorization];
        
    // 本地通知 + App未存活 (iOS 10.0以前)
    UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    if (notification.userInfo) {
        //  处理收到的通知
    }
    return YES;
}

/**
 检查本地通知的授权
 */
- (void)checkNotificationAuthorization {
    if (@available(iOS 10.0, *)) {
        UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
        // 这两个必须要写在上面  每次程序重新打开时都得设置一遍...
        center.delegate = self;
        //添加category: iOS 10.0以后
        [center setNotificationCategories:[self createNotificationCategoryActionsForNewVersion]];
        
        [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
            if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) {  // 判断是否是未选择授权状态
                // 若未授权, 则请求授权
                [center requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
                        if (granted) {
                              NSLog(@"授权开启成功");
                        } else {
                              NSLog(@"授权开启失败");
                        }
                }];
            }
        }];
    } else {
        //添加category: iOS 8.0以后
        UIUserNotificationCategory * category = [self adduserNotificationCategory];
        UIUserNotificationSettings * setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:[NSSet setWithObject:category]];
        [[UIApplication sharedApplication] registerUserNotificationSettings:setting];
    }
}

添加 category (即在通知栏重压时设置其按钮)


通知栏的通知
//添加category: iOS 10.0以后
-(NSSet *)createNotificationCategoryActionsForNewVersion{
    //注册本地通知用到的Action
    //进入app按钮
    UNNotificationAction * localAction = [UNNotificationAction actionWithIdentifier:kCategoryActionIdentifierOpen title:@"查看通知" options:UNNotificationActionOptionForeground];
    
    //取消按钮
    UNNotificationAction *localCancel = [UNNotificationAction actionWithIdentifier:kCategoryActionIdentifierCancel title:@"忽略" options:UNNotificationActionOptionDestructive];
    //将这些action带入category
    UNNotificationCategory *localCategory = [UNNotificationCategory categoryWithIdentifier:kUNCategoryIdentifier actions:@[localAction,localCancel] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];
    
    return [NSSet setWithObjects:localCategory,nil];
}

//添加category: iOS 8.0以后
- (UIMutableUserNotificationCategory *)adduserNotificationCategory {
    //UIMutableUserNotificationAction用来添加自定义按钮
    UIMutableUserNotificationAction * responseAction = [[UIMutableUserNotificationAction alloc] init];
    responseAction.identifier = kCategoryActionIdentifierOpen;
    responseAction.title = @"查看通知";
    responseAction.activationMode = UIUserNotificationActivationModeForeground; //点击的时候启动程序
    responseAction.authenticationRequired = YES;//需要解锁权限
    
    UIMutableUserNotificationAction *deleteAction = [[UIMutableUserNotificationAction alloc] init];
    deleteAction.identifier = kCategoryActionIdentifierCancel;
    deleteAction.title = @"忽略";
    deleteAction.activationMode = UIUserNotificationActivationModeBackground; //点击的时候不启动程序,后台处理
    deleteAction.destructive = YES; //YES为红色,NO为蓝色
    
    UIMutableUserNotificationCategory *category = [[UIMutableUserNotificationCategory alloc] init];
    category.identifier = kUNCategoryIdentifier;//用于将该 category 标识的同时,那一个 notification 实例需要使用这个 category 的 actions 也是传入这个值给 notification 的。
    
    //UIUserNotificationActionContextDefault:默认添加可以添加两个自定义按钮
    //UIUserNotificationActionContextMinimal:四个自定义按钮
    [category setActions:@[responseAction, deleteAction] forContext:UIUserNotificationActionContextDefault];
    
    return category;
}

添加通知
  这里是点击一个按钮事件来添加通知的:

- (IBAction)addLocalNotification:(UIButton *)sender {
    NSDictionary * dict = @{@"businessId": @"kLocalNotificationTest",@"title": @"本地通知测试",@"memo": @"通知发送成功", @"startTime": @"2019-10-28 10:10:10"};
    NSDictionary * dict1 = @{@"businessId": @"kLocalNotificationTestOne",@"title": @"本地通知测试__One",@"memo": @"通知发送成功__One", @"startTime": @"2019-10-28 10:10:10"};
    NSArray * arr = @[dict, dict1];
    // 添加两个通知
    for (NSDictionary * dataDic in arr) {
        NSData * data = [NSJSONSerialization dataWithJSONObject:dataDic options:NSJSONWritingPrettyPrinted error:nil];
        NSString * string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        [self addLocalNotificationWithData:string];
    }
}

/**
 添加本地通知

 @param dataString 通知的内容: 字典序列化后的字符串
 */
- (void)addLocalNotificationWithData: (NSString *)dataString {
    NSData * data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
    
    if (@available(iOS 10.0, *)) {
        UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
        [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
            if (settings.authorizationStatus == UNAuthorizationStatusDenied) {
                NSLog(@"用户未授权");
                return;
            }
            
            UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc] init];
            self.badgeNum++;
            content.badge = @(self.badgeNum);
            content.title = dict[@"title"];
            content.body = dict[@"memo"];
            content.userInfo = @{@"type" : kNotificationScheduleType, @"data": dict};;
            
            // 这个必须要加 且要和 UNNotificationCategory 的Identifier 保持一致
            content.categoryIdentifier = kUNCategoryIdentifier;
            
            UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5.f repeats:NO];
            
            UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:dict[@"businessId"] content:content trigger:trigger];
            [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
                if (error) {
                    NSLog(@"添加本地通知报错: %@", error);
                }
            }];
        }];
    } else {
        if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) {
            NSLog(@"用户未授权");
            return;
        }
        
        UILocalNotification * notification = [[UILocalNotification alloc] init];
        notification.timeZone = [NSTimeZone defaultTimeZone];
        notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:5.f];
        
        notification.alertTitle = dict[@"title"];
        notification.alertBody = dict[@"memo"];
        self.badgeNum++;
        notification.applicationIconBadgeNumber = self.badgeNum;
        notification.soundName = UILocalNotificationDefaultSoundName;
        notification.alertAction = @"查看";
        
        notification.userInfo = @{@"type" : kNotificationScheduleType, @"data": dict};;
        notification.category = kUNCategoryIdentifier;
        
        [[UIApplication sharedApplication] scheduleLocalNotification:notification];
    }
}

通知的回调处理:

  iOS10.0 后是通知代理来回调的 UNUserNotificationCenterDelegate

// App在前台获取通知
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler  API_AVAILABLE(ios(10.0)){
    completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound);
}

// 点击通知进入App
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler  API_AVAILABLE(ios(10.0)){
    completionHandler();
    
    NSDictionary * userInfo = response.notification.request.content.userInfo;
    if(userInfo[@"type"] && [userInfo[@"type"] isEqualToString:kNotificationScheduleType]) {
        if([response.actionIdentifier isEqualToString:kCategoryActionIdentifierCancel] || [response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier]) {
            // 通知栏点击 忽略 或 清除 时的回调
        } else {
            //  点击 打开 或 查看 时的回调
        }
    }
}

  iOS10.0 以前的通知回调:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 本地通知 + App未存活
    UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    if (notification.userInfo) {
        // 处理回调回来的通知事件
    }
    return YES;
}
// APP在前台运行中收到 本地通知 时调用, 以及App 处于后台挂起(suspended)状态,但未 terminated 时,点击通知启动都是这个方法进行响应
// 即 App 存活
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
    
}

//调用过用户注册通知方法之后执行(也就是调用完registerUserNotificationSettings:方法之后执行)
-(void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
    
}

// APP 将要进入前台时调用
- (void)applicationWillEnterForeground:(UIApplication *)application {
    //  设置App图标通知数量显示
}

//iOS 8 ~ 9 ,当点击本地通知自定义的响应按钮(action btn)时,根据按钮的 activeMode 模式,回调以下方法
//1. ActivationModeForeground 的 action , 会启动 App 同时回调方法
//2. ActivationModeBackground 的 action 不启动 App 让 App 在 background 下回调方法
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler {
    
    if ([identifier isEqualToString:@"kCategoryActionIdentifierOpen"]) {
        //ActivationModeForeground 的 action , 启动 App 让 App 在 Foreground 下响应
        
    } else {
        //ActivationModeBackground 的 action 不启动 App 让 App 在 background 下响应
        NSLog(@"%s  -- %@  -- identifier %@ --- thread %@", __func__, notification, identifier, [NSThread currentThread]);
        
//        //下面代码用于测试,退出 App 后接收到 本地通知时,点击后台action时是否执行了这个响应方法。实测执行了的
//        [[NSUserDefaults standardUserDefaults] setObject:@"ActivationModeBackground 的 action 不启动 App 让 App 在 background 下响应" forKey:@"IGNOREKEY"];
//        [[NSUserDefaults standardUserDefaults] synchronize];
        
    }
    completionHandler(); //根据Action btn 的 identifier 处理自定义事件后应该马上调用 completionHandler block,如果调用 completionHandler block 失败的话,App 会立即 terminated。
}

移除通知 或 移除指定通知

#pragma mark -- 移除本地通知
/**
 移除指定的通知

 @param identifierStr 通知的唯一标识 或 信息
 */
- (void)removeNotificationWithIdentifier:(NSString *)identifierStr {
    //获取本地通知数组 (该数组会持有需要重复 fired 的 已被 copy 的 notification 对象,用于到达下次间隔时再 fire, 如果不需要重复的 notification,即 notification.repeatInterval = 0 的话,该 notification fire 之后不会被 copy 保留到这个数组里)
    //本地通知最多只能有64个,超过会被系统忽略
    if (@available(iOS 10.0, *)) {
        [[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[identifierStr]];
    } else {
        NSArray *notifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
        for (UILocalNotification * notification in notifications) {
            NSString * businessId = notification.userInfo[@"businessId"];
            if ([identifierStr isEqualToString:businessId]) {
                [[UIApplication sharedApplication] cancelLocalNotification:notification];
            }
        }
    }
    
    /*
     执行取消本地通知的场景:
     1. 已经响应过的本地通知,需要取消。
     2. 已经递交到 application 的,但在 fire 之前 确定要取消的通知,需要取消。如提醒任务的取消,或更改提醒时间(此时应该是新的一个本地通知了)
     */
}

/**
 移除所有的通知
 */
- (void)removeAllNotification {
    if (@available(iOS 10.0, *)) {
        [[UNUserNotificationCenter currentNotificationCenter] removeAllPendingNotificationRequests];
    } else {
        [[UIApplication sharedApplication] cancelAllLocalNotifications];
    }
}

因代码是在项目里抽取的, 有不正之处欢迎指证...

***全部代码:

AppDelegate.h

#import "AppDelegate.h"
#import "LocalNotification/LocalNotificationManager.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [[LocalNotificationManager shareLocalNotification] checkNotificationAuthorization];
    
    [[LocalNotificationManager shareLocalNotification] removeAllNotification];
    
    // 本地通知 + App未存活
    UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    if (notification.userInfo) {
        [[NSUserDefaults standardUserDefaults] setObject:notification.userInfo forKey:@"localNotificationKey"];
        [[NSUserDefaults standardUserDefaults] synchronize];
        
    }

    return YES;
}

// APP在前台运行中收到 本地通知 时调用, 以及App 处于后台挂起(suspended)状态,但未 terminated 时,点击通知启动都是这个方法进行响应
// 即 App 存活
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
    [[LocalNotificationManager shareLocalNotification] receiveLocalNotificationWithUserInfo:notification.userInfo];
}

//调用过用户注册通知方法之后执行(也就是调用完registerUserNotificationSettings:方法之后执行)
-(void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
    
    NSLog(@"%s",__func__);
}

// APP 将要进入前台时调用
- (void)applicationWillEnterForeground:(UIApplication *)application {
    
    // 设置这个后, 后导致通知栏里的通知, 点击其中任何一个后, 其它通知都会消失...
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
    [LocalNotificationManager shareLocalNotification].badgeNum = 0;
}

//iOS 8 ~ 9 ,当点击本地通知自定义的响应按钮(action btn)时,根据按钮的 activeMode 模式,回调以下方法
//1. ActivationModeForeground 的 action , 会启动 App 同时回调方法
//2. ActivationModeBackground 的 action 不启动 App 让 App 在 background 下回调方法
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler {
    
    if ([identifier isEqualToString:@"kCategoryActionIdentifierOpen"]) {
        //ActivationModeForeground 的 action , 启动 App 让 App 在 Foreground 下响应
        [[LocalNotificationManager shareLocalNotification] receiveLocalNotificationWithUserInfo:notification.userInfo];
    } else {
        
        NSInteger badgeNumber = [LocalNotificationManager shareLocalNotification].badgeNum;
        badgeNumber = badgeNumber > 0 ? --badgeNumber : 0;
        [LocalNotificationManager shareLocalNotification].badgeNum = badgeNumber;
        [[UIApplication sharedApplication] setApplicationIconBadgeNumber:badgeNumber];
        
        //ActivationModeBackground 的 action 不启动 App 让 App 在 background 下响应
        NSLog(@"%s  -- %@  -- identifier %@ --- thread %@", __func__, notification, identifier, [NSThread currentThread]);
        
//        //下面代码用于测试,退出 App 后接收到 本地通知时,点击后台action时是否执行了这个响应方法。实测执行了的
//        [[NSUserDefaults standardUserDefaults] setObject:@"ActivationModeBackground 的 action 不启动 App 让 App 在 background 下响应" forKey:@"IGNOREKEY"];
//        [[NSUserDefaults standardUserDefaults] synchronize];
        
    }
    
    completionHandler(); //根据Action btn 的 identifier 处理自定义事件后应该马上调用 completionHandler block,如果调用 completionHandler block 失败的话,App 会立即 terminated。

}

@end

// 抽取的通知管理单例

LocalNotificationManager.h

#import 

NS_ASSUME_NONNULL_BEGIN

typedef void(^LocalNotificationManagerBlock)(NSString * userInfoString);

@interface LocalNotificationManager : NSObject

// 消息的数量
@property (nonatomic, assign, readwrite) NSInteger badgeNum;
@property (nonatomic, copy) LocalNotificationManagerBlock managerBlock;


+ (instancetype)shareLocalNotification;


/**
 检查本地通知的授权
 */
- (void)checkNotificationAuthorization;

/**
 添加本地通知

 @param dataString 通知的内容: 字典序列化后的字符串
 */
- (void)addLocalNotificationWithData: (NSString *)dataString;


/**
 移除指定的通知

 @param identifierStr 通知的唯一标识 或 信息
 */
- (void)removeNotificationWithIdentifier: (NSString *)identifierStr;


/**
 移除所有的通知
 */
- (void)removeAllNotification;


/**
 收到通知后的回调

 @param block block回调
 */
- (void)handleLocalNotificationWithBlock: (LocalNotificationManagerBlock)block;


/**
 收到通知后的数据处理

 @param userInfo 通知携带的数据
 */
- (void)receiveLocalNotificationWithUserInfo: (NSDictionary *)userInfo;


@end

NS_ASSUME_NONNULL_END


LocalNotificationManager.m

#import "LocalNotificationManager.h"
#import 
#import 

static NSString * const kNotificationScheduleType = @"kNotificationScheduleType";
static NSString * const kUNCategoryIdentifier = @"kUNCategoryIdentifier";
static NSString * const kCategoryActionIdentifierOpen = @"kCategoryActionIdentifierOpen";
static NSString * const kCategoryActionIdentifierCancel = @"kCategoryActionIdentifierCancel";

@interface LocalNotificationManager()


@end

@implementation LocalNotificationManager

+ (instancetype)shareLocalNotification {
    static LocalNotificationManager * _instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[LocalNotificationManager alloc] init];
    });
    return _instance;
}

- (void)checkNotificationAuthorization {
    if (@available(iOS 10.0, *)) {
        UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
        // 这两个必须要写在上面  每次程序重新打开时都得设置一遍...
        center.delegate = self;
        [center setNotificationCategories:[self createNotificationCategoryActionsForNewVersion]];
        
        [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
            if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) {  // 判断是否是未选择授权状态
                [center requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
                }];
            }
        }];
    } else {
        UIUserNotificationCategory * category = [self adduserNotificationCategory];
        UIUserNotificationSettings * setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:[NSSet setWithObject:category]];
        [[UIApplication sharedApplication] registerUserNotificationSettings:setting];
    }
}

- (void)addLocalNotificationWithData: (NSString *)dataString {
    NSData * data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
    
    NSTimeInterval noticInterval = [self diffNowDateWithNotificationTime:dict[@"startTime"]];
    if (noticInterval <= 0) {
        return;
    }
    
    if (@available(iOS 10.0, *)) {
        UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
        [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
            if (settings.authorizationStatus == UNAuthorizationStatusDenied) {
                NSLog(@"用户未授权");
                return;
            }
            
            UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc] init];
            self.badgeNum++;
            content.badge = @(self.badgeNum);
            content.title = dict[@"title"];
            content.body = dict[@"memo"];
            content.userInfo = @{@"type" : kNotificationScheduleType, @"data": dict};;
            
            // 这个必须要加 且要和 UNNotificationCategory 的Identifier 保持一致
            content.categoryIdentifier = kUNCategoryIdentifier;
            
            UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5.f repeats:NO];
            
            UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:dict[@"businessId"] content:content trigger:trigger];
            [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
                if (error) {
                    NSLog(@"添加本地通知报错: %@", error);
                }
            }];
        }];
    } else {
        if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) {
            NSLog(@"用户未授权");
            return;
        }
        
        UILocalNotification * notification = [[UILocalNotification alloc] init];
        notification.timeZone = [NSTimeZone defaultTimeZone];
        notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:5.f];
        
        notification.alertTitle = dict[@"title"];
        notification.alertBody = dict[@"memo"];
        self.badgeNum++;
        notification.applicationIconBadgeNumber = self.badgeNum;
        notification.soundName = UILocalNotificationDefaultSoundName;
        notification.alertAction = @"查看";
        
        notification.userInfo = @{@"type" : kNotificationScheduleType, @"data": dict};;
        notification.category = kUNCategoryIdentifier; // 与下面创建的category 的identifier必须保持一致
        
        [[UIApplication sharedApplication] scheduleLocalNotification:notification];
    }
}

//添加category: iOS 10.0以后
-(NSSet *)createNotificationCategoryActionsForNewVersion{
    //注册本地通知用到的Action
    //进入app按钮
    UNNotificationAction * localAction = [UNNotificationAction actionWithIdentifier:kCategoryActionIdentifierOpen title:@"查看通知" options:UNNotificationActionOptionForeground];
    
    //取消按钮
    UNNotificationAction *localCancel = [UNNotificationAction actionWithIdentifier:kCategoryActionIdentifierCancel title:@"忽略" options:UNNotificationActionOptionDestructive];
    //将这些action带入category
    UNNotificationCategory *localCategory = [UNNotificationCategory categoryWithIdentifier:kUNCategoryIdentifier actions:@[localAction,localCancel] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];
    
    return [NSSet setWithObjects:localCategory,nil];
}

//添加category: iOS 8.0以后
- (UIMutableUserNotificationCategory *)adduserNotificationCategory {
    //UIMutableUserNotificationAction用来添加自定义按钮
    UIMutableUserNotificationAction * responseAction = [[UIMutableUserNotificationAction alloc] init];
    responseAction.identifier = kCategoryActionIdentifierOpen;
    responseAction.title = @"查看通知";
    responseAction.activationMode = UIUserNotificationActivationModeForeground; //点击的时候启动程序
    responseAction.authenticationRequired = YES;//需要解锁权限
    
    UIMutableUserNotificationAction *deleteAction = [[UIMutableUserNotificationAction alloc] init];
    deleteAction.identifier = kCategoryActionIdentifierCancel;
    deleteAction.title = @"忽略";
    deleteAction.activationMode = UIUserNotificationActivationModeBackground; //点击的时候不启动程序,后台处理
    deleteAction.destructive = YES; //YES为红色,NO为蓝色
    
    UIMutableUserNotificationCategory *category = [[UIMutableUserNotificationCategory alloc] init];
    category.identifier = kUNCategoryIdentifier;//用于将该 category 标识的同时,那一个 notification 实例需要使用这个 category 的 actions 也是传入这个值给 notification 的。
    
    //UIUserNotificationActionContextDefault:默认添加可以添加两个自定义按钮
    //UIUserNotificationActionContextMinimal:四个自定义按钮
    [category setActions:@[responseAction, deleteAction] forContext:UIUserNotificationActionContextDefault];
    
    return category;
}


#pragma mark -- UNUserNotificationCenterDelegate
// App在前台获取通知
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler  API_AVAILABLE(ios(10.0)){
    completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound);
}

// 点击通知进入App
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler  API_AVAILABLE(ios(10.0)){
    completionHandler();
    
    NSDictionary * userInfo = response.notification.request.content.userInfo;
    if(userInfo[@"type"] && [userInfo[@"type"] isEqualToString:kNotificationScheduleType]) {
        if([response.actionIdentifier isEqualToString:kCategoryActionIdentifierCancel] || [response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier]) {
            self.badgeNum = self.badgeNum > 0 ? --self.badgeNum : 0;
            [[UIApplication sharedApplication] setApplicationIconBadgeNumber:self.badgeNum];
        } else {
            [self receiveLocalNotificationWithUserInfo:userInfo[@"data"]];
        }
    }
}

#pragma mark -- 移除本地通知
- (void)removeNotificationWithIdentifier:(NSString *)identifierStr {
    
    //获取本地通知数组 (该数组会持有需要重复 fired 的 已被 copy 的 notification 对象,用于到达下次间隔时再 fire, 如果不需要重复的 notification,即 notification.repeatInterval = 0 的话,该 notification fire 之后不会被 copy 保留到这个数组里)
    //本地通知最多只能有64个,超过会被系统忽略
    if (@available(iOS 10.0, *)) {
        [[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[identifierStr]];
    } else {
        NSArray *notifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
        for (UILocalNotification * notification in notifications) {
            NSString * businessId = notification.userInfo[@"businessId"];
            if ([identifierStr isEqualToString:businessId]) {
                [[UIApplication sharedApplication] cancelLocalNotification:notification];
            }
        }
    }
    
    
    //删除指定通知
    //    [[UIApplication sharedApplication] cancelLocalNotification:notifications[0]];
    //删除所有通知
//    [[UIApplication sharedApplication] cancelAllLocalNotifications];
    
    /*
     执行取消本地通知的场景:
     1. 已经响应过的本地通知,需要取消。
     2. 已经递交到 application 的,但在 fire 之前 确定要取消的通知,需要取消。如提醒任务的取消,或更改提醒时间(此时应该是新的一个本地通知了)
     */
}

- (void)removeAllNotification {
    self.badgeNum = 0;
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
    if (@available(iOS 10.0, *)) {
        [[UNUserNotificationCenter currentNotificationCenter] removeAllPendingNotificationRequests];
    } else {
        [[UIApplication sharedApplication] cancelAllLocalNotifications];
    }
}

#pragma mark -- 处理收到的通知
- (void)handleLocalNotificationWithBlock:(LocalNotificationManagerBlock)block {
    NSDictionary * dict = [[NSUserDefaults standardUserDefaults] objectForKey:@"localNotificationKey"];
    if (dict) {
        [self receiveLocalNotificationWithUserInfo:dict];
    }
    self.managerBlock = block;
}

- (void)receiveLocalNotificationWithUserInfo:(NSDictionary *)userInfo {
    if (!userInfo) {
        [self showInfo:@"消息失败"];
        return;
    }
    self.badgeNum = self.badgeNum > 0 ? --self.badgeNum : 0;
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:self.badgeNum];
    
    NSData * data = [NSJSONSerialization dataWithJSONObject:userInfo options:NSJSONWritingPrettyPrinted error:nil];
    NSString * string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    [self showInfo:string];
}

// 通知消息的显示
- (void)showInfo:(NSString *)infoStr {
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:infoStr preferredStyle:UIAlertControllerStyleAlert];
    
    [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:NULL]];
    
    [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}

// 获取当前时间与指定时间之间的时间戳
- (CGFloat)diffNowDateWithNotificationTime: (NSString *)timeStr {
    NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:@"Asia/Beijing"];
    [formatter setTimeZone:timeZone];
    
    NSDate * noticDate = [formatter dateFromString:timeStr];
    
    NSTimeInterval noticInterval = [noticDate timeIntervalSince1970] * 1;
    NSTimeInterval nowInterval = [[NSDate date] timeIntervalSince1970] * 1;
    
    return noticInterval - nowInterval;
}
@end

添加通加的数据格式:

- (IBAction)addLocalNotification:(UIButton *)sender {
    NSDictionary * dict = @{@"businessId": @"kLocalNotificationTest",@"title": @"本地通知测试",@"memo": @"通知发送成功", @"startTime": @"2019-10-28 10:10:10"};
    NSDictionary * dict1 = @{@"businessId": @"kLocalNotificationTestOne",@"title": @"本地通知测试__One",@"memo": @"通知发送成功__One", @"startTime": @"2019-10-28 10:10:10"};
    NSArray * arr = @[dict, dict1];
    
    for (NSDictionary * dataDic in arr) {
        NSData * data = [NSJSONSerialization dataWithJSONObject:dataDic options:NSJSONWritingPrettyPrinted error:nil];
        NSString * string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        [[LocalNotificationManager shareLocalNotification] addLocalNotificationWithData:string];
    }
}

你可能感兴趣的:(本地推送通知)