史上最简单的消息推送

在iOS开发的过程中,涉及到两种推送,一种是本地推送,另一种就是远程推送。本地推送主要是在没有网络的情况下对用户进行事件或者消息的通知推送,例如系统自带的时间提醒和闹钟。远程推送则是必须要有网的情况下和苹果的APNs服务器建立长连接,从而推送消息,例如微信在后台的情况下,好友发来的信息,就是远程推送的内容。

推送.jpg

一、本地推送

注:一个App最多只能设置64个本地推送,当超过此限制的时候,系统会自动忽略多余的本地推送,而保留能最快触发的64个。循环的本地推送会被系统认为是同一个本地推送。

  1. iOS 10之前的本地推送UILocalNotification

1.1 注册本地推送

UIUserNotificationType type = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
    UIUserNotificationSettings *setings = [UIUserNotificationSettings settingsForTypes:type categories:nil];
    
    [[UIApplication sharedApplication] registerUserNotificationSettings:setings];

1.2 设置UILocalNotification的基本属性

UILocalNotification *loc = [[UILocalNotification alloc] init];
    if (loc) {
        loc.fireDate = [NSDate dateWithTimeIntervalSinceNow:10];
        loc.timeZone = [NSTimeZone defaultTimeZone];
        loc.alertBody = @"床前明月光";
        loc.repeatInterval = NSCalendarUnitMinute;
        loc.alertAction = NSLocalizedString(@"jump", nil);
        loc.alertTitle = NSLocalizedString(@"静夜思", nil);
        loc.soundName = UILocalNotificationDefaultSoundName;
        loc.applicationIconBadgeNumber = 1;
        loc.alertLaunchImage = @"1" ;
        
    }
    //调用此方法开启推送
    [[UIApplication sharedApplication] scheduleLocalNotification:loc];
静夜思.jpg

其中UILocalNotification的属性作用为

fireDate:启动时间
timeZone:启动时间参考的时区
repeatInterval:重复推送时间(NSCalendarUnit类型),0代表不重复
repeatCalendar:重复推送时间(NSCalendar类型)
alertBody:通知内容
alertAction:解锁滑动时的事件
alertLaunchImage:启动图片,设置此字段点击通知时会显示该图片
alertTitle:通知标题
applicationIconBadgeNumber:收到通知时App icon的角标
soundName:推送是带的声音提醒,设置默认的字段为UILocalNotificationDefaultSoundName
userInfo:发送通知时附加的内容
category:此属性和注册通知类型时有关联
 
region:带有定位的推送相关属性
regionTriggersOnce:带有定位的推送相关属性

1.3 取消推送的方法

//  取消某一个本地推送
[[UIApplication sharedApplication] cancelLocalNotification:notification];
//  取消所有的本地推送
[[UIApplication sharedApplication] cancelAllLocalNotifications];

1.4点击推送消息所调用事件

//点击推送消息进入前台之后所要走的方法,当应用一直在前台的情况下每次进行推送都会触发这个方法
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
    NSLog(@"%@",notification);
}

1.5带有定位功能的本地推送,这个要配合CLLocation一起使用

//设置位置管理者进行位置跟踪
CLLocationManager *locManager = [[CLLocationManager alloc] init];
    locManager.delegate = self;
    [locManager requestWhenInUseAuthorization];
//在位置信息改变的 时候调用下面的方法
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
    //检查状态判断App是否授权
    NSLog(@"%d",status);
    if (status == kCLAuthorizationStatusAuthorizedWhenInUse) {
        //  设置通知
        [self locationNotificWithlocation];
    }
}
//查看是否到了指定的位置
- (void)locationNotificWithlocation
{
    UILocalNotification *locNotification = [[UILocalNotification alloc] init];
    
    locNotification.alertBody = @"到达目的地!";
    locNotification.regionTriggersOnce = YES;
    CLLocationCoordinate2D loc = CLLocationCoordinate2DMake(22.28,114.15);
    locNotification.region = [[CLRegion alloc] initCircularRegionWithCenter:loc radius:1000000 identifier:@"北京"];
    
    [[UIApplication sharedApplication] scheduleLocalNotification:locNotification];
}
//发送通知之后调用下面的方法
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
    NSLog(@"%@",notification);
    if (notification.region) {
        NSLog(@"%@",notification.region);
    }
}

注:本地推送无论是在App处于什么状态,当有消息的时候,在手机下拉的Notifications中都是会有显示的。当App不是处于Foreground(前台)状态的时候,都是会有banner(横幅的弹窗)通知的,但是当App处于Foreground状态时,通知还是会一样发出,而且手机下拉Notifications中会有磁条通知,但是banner并不会出现。这样就会引起一个时间错觉问题。

2.iOS 10之后的本地推送UNUserNotificationCenter
iOS 10的时候系统废弃了之前的UILocalNotification,用专门的UNUserNotificationCenter处理推送的通知消息。
下面就是iOS 10本地通知的发送方法

//1. 注册通知
// 使用 UNUserNotificationCenter 来管理通知
    UNUserNotificationCenter *centers = [UNUserNotificationCenter currentNotificationCenter];
    //监听回调事件
    centers.delegate = self;
    
    //iOS 10 使用以下方法注册,才能得到授权
    [centers requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge)
                          completionHandler:^(BOOL granted, NSError * _Nullable error) {
                              // Enable or disable features based on authorization.
                          }];
    
    //获取当前的通知设置,UNNotificationSettings 是只读对象,不能直接修改,只能通过以下方法获取
    [centers getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
        
    }];

//2. 创建通知对象
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    center.delegate = self;
    
    UNMutableNotificationContent *noticontent = [[UNMutableNotificationContent alloc] init];
    noticontent.body = @"鹅鹅鹅,曲项向天歌";
    noticontent.badge = @1;
    noticontent.title = @"咏鹅";
    noticontent.userInfo = @{@"锄禾" : @"锄禾日当午"};
//3. 设置触发机制
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:10 repeats:0];
//4. 创建UNNotificationRequest通知请求对象
UNNotificationRequest *req = [UNNotificationRequest requestWithIdentifier:@"id" content:noticontent trigger:trigger];
//5. 通知中心发送通知
[center addNotificationRequest:req withCompletionHandler:^(NSError * _Nullable error) {
        
    }];
//在设置UNUserNotificationCenterDelegate之后,还可以监听通知的发送和接收事件
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
{
    
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler
{
    
}

咏鹅.jpg

注:在iOS10里面进行通知发送的时候要注意导入<UserNotifications/UserNotifications.h>这个类库,它包含几个新的类

  1. UNMutableNotificationContent通知内容
    通知内容就是设定通知的一些展示信息,iOS10之后可以设置subtitle。除了之前包含的内容外,声音的设置需要借助一个新类UNNotificationSound,通知文件要放到bundle里面。另外在实际的测试过程中发现,添加通知的声音有时候会无效。这应该是iOS10存在的一个bug,删除掉程序,再安装运行就好了。
  2. UNNotificationTrigger
    Trigger是新加入的一个功能,通过此类可设置本地通知触发条件。它一共有一下几种类型:
    1.UNPushNotificaitonTrigger 推送服务的Trigger,由系统创建
    2.UNTimeIntervalNotificaitonTrigger 时间触发器,可以设置多长时间以后触发,是否重复。如果设置重复,重复时长要大于60s
  1. UNCalendarNotificaitonTrigger 日期触发器,可以设置某一日期触发事件
  1. UNLocationNotificaitonTrigger 位置触发器,用于进行位置推送,到达指定范围之后,触发通知。通过CLRegion设定具体范围
  1. UNUserNotificationCenter通知中心 获取通知[UNUserNotificationCenter currentNotificationCenter],然后通过addNotificaitonRequest:就完成了一个通知的添加。

二、 远程推送

iOS远程推送的流程如下图:
iOS远程推送原理.jpg
  1. 应用程序注册推送通知后,手机向APNS获取device token
  2. APNS将对应手机的device token 发送给注册通知的应用
  3. 注册通知的应用将device token发送给自己应用的服务器(若是第三方的推送服务则发送到相应的推送端服务器)储存起来
  4. 若要发送推送通知给用户则通过携带服务器储存的用户device token和推送详情发送给APNS
  5. APNS通过判断device token将推送通知发送给对应的手机应用程序

要想完成远程推送,必不可少的就是对应项目的.p12文件,下面展示下证书的申请流程:

1. 从钥匙串里的证书颁发机构申请CSR文件.png

2. 申请推送项目的APP ID.png

3.申请推送证书.png

4选取对应的APP ID.png

5.选取申请的CSR证书.png

6.将cer文件拖进钥匙串,添加到如图所示的位置.png

7.右击选取对应的证书导出.png

8.导出所需要的.p12文件到指定位置.png

保存好申请的.p12文件备用,特别提醒,生成.p12证书的密码要记得。

A. 通过自己的服务器推送通知

注:生成好的.p12文件发送给服务器就好,服务器同事会自行处理

// 注册远程通知
CGFloat version = [[[UIDevice currentDevice] systemVersion] floatValue];
        
        if (version >= 10.0)
        {
            UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
            [center requestAuthorizationWithOptions:UNAuthorizationOptionCarPlay | UNAuthorizationOptionSound | UNAuthorizationOptionBadge | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
                
                if (granted) {
                    NSLog(@" iOS 10 request notification success");
                }else{
                    NSLog(@" iOS 10 request notification fail");
                }
            }];
        }
        else if (version >= 8.0)
        {
            UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeSound | UIUserNotificationTypeBadge | UIUserNotificationTypeAlert categories:nil];
            [application registerUserNotificationSettings:setting];
        }else{
//            iOS 7及以下
        UIRemoteNotificationType type = UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound;
        [application registerForRemoteNotificationTypes:type];
    }
        
        //注册通知
        [[UIApplication sharedApplication] registerForRemoteNotifications];

// 发送deviceToken
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
    // 将deviceToken转换成字符串并且通过网络请求发送到服务器
    NSString* dt = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
    NSString *token = [dt stringByReplacingOccurrencesOfString:@" " withString:@""];
     
      // 这里发个网络请求把deviceToken传给后台
}
// error
-(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
    
}
// 消息
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    // 可对userInfo进行处理
}
#pragma mark  iOS 10 获取推送信息 UNUserNotificationCenterDelegate  
  
//APP在前台的时候收到推送的回调  
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler  
{  
    UNNotificationContent *content =  notification.request.content;  
    NSDictionary *userInfo = content.userInfo;  
      
    [self handleRemoteNotificationContent:userInfo];  
      
    //可以执行设置 弹窗 和 声音  
    completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionSound);  
}  
//APP在后台,点击推送信息,进入APP后执行的回调  
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler  
{  
    UNNotificationContent *content  = response.notification.request.content;  
    NSDictionary *userInfo = content.userInfo;  
      
    [self handleRemoteNotificationContent:userInfo];  
  
    completionHandler();  
}  
  
- (void)handleRemoteNotificationContent:(NSDictionary *)userInfo  
{  
    NSLog(@" iOS 10 after Notificatoin message:\n %@",userInfo);  
}

#pragma mark iOS 10 之前 获取通知的信息  
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo  
{  
    NSLog(@"iOS 10 before Notification message。\n  %@",userInfo);  
} 

特别提醒!!!推送的时候要打开下图位置的开关,不论是哪种远程推送的方法

需要打开的位置一.png

需要打开的位置二.png

B. 第三方服务器推送服务(以极光推送为例)

首先,成为第三方推送服务的开发者,并根据创建的.p12文件创建应用
1. 登录到极光推送的官网注册为开发者并登录.png

2. 创建应用.png

3.上传相应的.p12文件和文件密码.png
其次,下载对应的SDK,并将Lib文件拖入项目包内
下载相应的SDK.png
最后,根据开发者文档编写代码
//以下代码均粘贴自开发者文档
- (void)Jpush:(NSDictionary *)launchOptions
{
    //Required
    //notice: 3.0.0及以后版本注册可以这样写,也可以继续用之前的注册方式
    JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];
    entity.types = JPAuthorizationOptionAlert|JPAuthorizationOptionBadge|JPAuthorizationOptionSound;
    if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
        // 可以添加自定义categories
        // NSSet<UNNotificationCategory *> *categories for iOS10 or later
        // NSSet<UIUserNotificationCategory *> *categories for iOS8 and iOS9
    }
    [JPUSHService registerForRemoteNotificationConfig:entity delegate:self];
    
    /*
     appKey
     填写管理Portal上创建应用后自动生成的AppKey值。请确保应用内配置的 AppKey 与 Portal 上创建应用后生成的 AppKey 一致。
     channel
     指明应用程序包的下载渠道,为方便分渠道统计,具体值由你自行定义,如:App Store。
     apsForProduction
     1.3.1版本新增,用于标识当前应用所使用的APNs证书环境。
     0 (默认值)表示采用的是开发证书,1 表示采用生产证书发布应用。
     注:此字段的值要与Build Settings的Code Signing配置的证书环境一致
     */
    NSString *appkey = @"你创建应用的appkey";
    [JPUSHService setupWithOption:launchOptions appKey:appkey
                          channel:@"channel"
                 apsForProduction:0
            advertisingIdentifier:nil];
}
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    
    /// Required - 注册 DeviceToken
    [JPUSHService registerDeviceToken:deviceToken];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    //Optional
    NSLog(@"did Fail To Register For Remote Notifications With Error: %@", error);
}
// 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三种类型可以选择设置
}

// 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();  // 系统要求执行这个方法
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    
    // Required, iOS 7 Support
    [JPUSHService handleRemoteNotification:userInfo];
    completionHandler(UIBackgroundFetchResultNewData);
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    
    // Required,For systems with less than or equal to iOS6
    [JPUSHService handleRemoteNotification:userInfo];
}
最最后,进行推送测试
1.在极光的控制台上发送需要推送的消息.png
2.手机接收到推送的消息.png

楼主吐血码完,腰酸背痛腿抽筋,能看完的都是亲人,那么就请亲人点个赞啦,拜谢!!!

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

推荐阅读更多精彩内容

  • 极光推送: 1.JPush当前版本是1.8.2,其SDK的开发除了正常的功能完善和扩展外也紧随苹果官方的步伐,SD...
    Isspace阅读 6,701评论 10 16
  • 概述 在多数移动应用中任何时候都只能有一个应用程序处于活跃状态,如果其他应用此刻发生了一些用户感兴趣的那么通过通知...
    莫离_焱阅读 6,498评论 1 8
  • 来源:崔江涛的博客 概述在多数移动应用中任何时候都只能有一个应用程序处于活跃状态,如果其他应用此刻发生了一些用户感...
    李栖桐阅读 922评论 0 0
  • 大学的第一年就这样结束了。在这一年当中,我学会了很多东西,不仅仅是书上的知识,还有许多实际生活中该有的态度等等,...
    CCCCSJ阅读 635评论 0 1
  • 草案:2017 年 6 月 26 日 (@dayzh (https://steemit.com/@dayzh)) ...
    区块链生存指南阅读 1,929评论 0 4