iOS学习笔记22-推送通知

一、推送通知

推送通知就是向用户推送一条信息来通知用户某件事件,可以在应用退到后台后,或者关闭后,能够通过推送一条消息通知用户某件事情,比如版本更新等等。

推送通知的常用应用场景:
  • 一些任务管理APP,会到任务时间即将到达时,通知你该做任务了。
  • 健身APP定时提醒你应该健身了。
  • 买过电影票,提前半个小时通知你,电影即将开场。
  • 当你QQ或微信收到信息时,即使退到后台,也可以收到信息通知你。
  • 电商APP,推送一条消息通知我们有新品上架等等。
推送通知的常用展示样式:
  1. 屏幕顶部显示一块横幅
  1. 在锁屏界面显示一块横幅
  2. 更新APP图标数字
  3. 播放音效
  4. 屏幕中间弹出一个UIAlertView
推送通知分为:
  1. 本地推送通知:
    不需要联网,在APP代码中推送的通知,确定知道未来某个时间点应该提醒用户什么
    【开发人员在APP内部通过代码发生 = 本地推送通知】
  1. 远程推送通知:
    需要联网,是由服务器推送的通知,不确定未来某个时间点应该提醒用户什么
    【服务器可以确定通知时间和内容 = 远程推送通知】

使用原则:谁确定通知时间和内容,谁就可以发生

二、本地推送通知

本地推送通知步骤:
  1. 在iOS8以后使用本地推送通知,需要得到用户的许可
  1. 创建UILocalNotification本地通知对象,并设置必要属性
  2. 开始本地推送通知:
    第一种方法,延时推送,根据本地通知对象的fireDate设置进行本地推送通知
[[UIApplication shareApplication] scheduleLocalNotification:notification];

第二种方法,立刻推送,忽略本地通知对象的fireDate设置进行本地推送通知

[[UIApplication shareApplication] presentLocalNotificationNow:notification];
  1. 监听用户点击通知:
  • APP处于前台,此时不会弹框通知用户,但会调用对应的代理方法 :
-(void)application:(UIApplication *)application didReceiveLocalNotification;
  • APP处于后台,屏幕上方会弹出横幅,用户点击横幅后,会进入前台,调用上面的代理方法。
  • APP已关闭,屏幕上方会弹出横幅,用户点击横幅后,会启动APP,调用以下方法:
-(BOOL)application:(UIApplication *)application 
            didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
/* 通过参数launchOptions获取本地推送通知内容 */
UILocalNotification *local = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
  1. 调用UIApplication的对象方法,取消本地推送通知:
/* 取消指定的本地推送通知 */
-(void)cancelLocalNotification:(UILocalNotification *)notification;
/* 取消全部本地推送通知 */
-(void)cancelAllLocalNotification;
以下是实例代码:
1. 注册通知代码以及UIAlertView显示通知方法代码
- (BOOL)application:(UIApplication *)application 
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
 {
    //因为是storyboard启动,这里就没有其他启动代码了

    //iOS8.0以后,如果需要使用推送通知,需要得到用户许可
    if (application.currentUserNotificationSettings.types == UIUserNotificationTypeNone) {
        //注册通知,有横幅通知、应用数字通知、应用声音通知
        UIUserNotificationSettings * setting = 
              [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert |
                                                           UIUserNotificationTypeBadge |
                                                           UIUserNotificationTypeSound
                                                categories:nil];
        [application registerUserNotificationSettings:setting];
    } else {
        //当APP关闭后接收到通知,在启动中获取本地推送通知对象
        UILocalNotification *notification = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
        [self showLocalNotification:notification];
    }
    return YES;
}
/* 弹框UIAlertView显示本地通知的信息 */
- (void)showLocalNotification:(UILocalNotification *)notification
{
    /* 显示本地通知 */
    NSDictionary *userInfo = notification.userInfo;
    NSString *title = @"本地通知";
    NSString *msg = userInfo[@"msg"];
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title 
                                                    message:msg 
                                                   delegate:nil 
                                          cancelButtonTitle:@"取消" 
                                          otherButtonTitles:@"确定", nil];
    [alert show];
    //移除本地通知
    [[UIApplication sharedApplication] cancelLocalNotification:notification];
}
2. 创建本地通知代码
/* 创建一个本地通知 */
- (UILocalNotification *)makeLocalNotification{
    //创建本地推送通知对象
    UILocalNotification *notification = [[UILocalNotification alloc] init];
    //设置调用时间
    notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:10.0];//通知触发的时间,10s以后
    notification.repeatInterval = NSCalendarUnitMinute;//每隔多久重复发一次本地通知
    //设置通知属性
    notification.alertBody = @"最近添加了诸多有趣的特性,是否立即体验?";//通知主体
    notification.applicationIconBadgeNumber = 1;//应用程序图标右上角显示的消息数
    notification.alertAction = @"打开应用"; //待机界面的滑动动作提示
    notification.alertLaunchImage = @"Default";//通过点击通知打开应用时的启动图片,这里使用程序启动图片
    notification.soundName = UILocalNotificationDefaultSoundName;//收到通知时播放的声音,默认消息声音
    //设置用户信息
    notification.userInfo = @{ @"id":@1, 
                               @"user":@"Kenshin Cui", 
                               @"msg":@"我来了一发本地通知"};//绑定到通知上的其他附加信息
    return notification;
}

如果需要每天的中午12点准时本地推送怎么办呢?
就像这么办,修改fireDaterepeatInterval属性

NSDateFormatter *formatter1 = [[NSDateFormatter alloc]init];  
[formatter setDateFormat:@"yyyy-MM-dd HH-mm-sss"];  
NSDate *resDate = [formatter dateFromString:@"2016-04-09 12-00-00"];
notification.fireDate = resDate;//设定为明天中午12点触发通知
//记得设置当前时区,没有设置的话,fireDate将不考虑时区,这样的通知会不准确
notification.timeZone = [NSTimeZone defaultTimeZone];
notification.repeatInterval = NSCalendarUnitDay;//每隔一天触发一次

3. 监听用户点击
/* 注册本地通知完成会调用,即用户点击确定授权后调用 */
- (void)application:(UIApplication *)application 
        didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
    //在这里我们尝试发送本地推送通知
    if (notificationSettings.types != UIUserNotificationTypeNone) {
        UILocalNotification *notification = [self makeLocalNotification];
        //延迟调用通知
        [application scheduleLocalNotification:notification];
        //立刻发送通知
        //[application presentLocalNotificationNow:notification];
    }
}
/* 应用还在运行,无论前台还是后台,都会调用该方法处理通知 */
- (void)application:(UIApplication *)application 
        didReceiveLocalNotification:(UILocalNotification *)notification
{
    if( notification ) {
        [self showLocalNotification:notification];
    }
}
/* 应用进入前台,去除应用边角数字显示 */
- (void)applicationWillEnterForeground:(UIApplication *)application {
    //去除应用边角数字
    [application setApplicationIconBadgeNumber:0];
}
本地通知弹出通告栏和应用边角数字变化
显示本地通知

三、远程推送通知

iOS远程消息推送的工作机制
iOS远程消息推送步骤:
  1. 应用服务提供商从服务器端把要发送的消息设备令牌(device token)发送给苹果的消息推送服务器APNs
  1. APNs根据设备令牌在已注册的设备(iPhone、iPad、iTouch、Mac等)查找对应的设备,将消息发送给相应的设备。
  2. 客户端设备接将接收到的消息传递给相应的应用程序,应用程序根据用户设置弹出通知消息。
下面是更详细的流程:
远程推送详细流程图

所有的苹果设备,在联网状态下,都会和苹果服务器APNs建立一个长连接

  • 长连接:服务器可以向客户端发送消息,保证数据的即时性,但比较占用资源
  • 短连接:服务器无法主动向客户端发消息,会话结束后,就立即释放资源,节省资源

远程推送通知就是借助苹果设备与APNs服务器之间的长连接,借助APNs服务器讲消息发送给客户端。

远程推送通知实现的条件:
  1. 必须有真机,只有真机具备UDID,才能生成deviceToken设备令牌
  1. 需要开发推送Cer证书

证书的申请请参考:iOS学习笔记21-推送证书与秘钥申请

deviceToken的生成算法只有Apple掌握,为了确保算法发生变化后仍然能够正常接收服务器端发送的通知,每次应用程序启动都重新获得deviceToken

远程推送通知步骤:
  1. iOS8以后,使用远程通知,需要请求用户授权
  1. 注册远程通知成功后会调用以下方法,获取deviceToken设备令牌:
-(void)application:(UIApplication *)application 
          didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken;
  1. deviceToken设备令牌发送给服务器,时刻保持deviceToken是最新的
  2. 监听远程推送通知:
-(void)application:(UIApplication *)application 
          didReceiveRemoteNotification:(NSDictionary *)userInfo;
下面是实例代码:
1. 注册远程推送通知代码:
- (BOOL)application:(UIApplication *)application 
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
   //iOS8.0以后,如果需要使用本地推送通知,需要得到用户许可
    if (![application isRegisteredForRemoteNotifications]) {
        UIUserNotificationSettings * setting = 
              [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert |
                                                           UIUserNotificationTypeBadge |
                                                           UIUserNotificationTypeSound
                                                categories:nil];
        [application registerUserNotificationSettings:setting];
        //注册远程推送通知
        [application registerForRemoteNotifications];
    }
    return YES;
}
2. 注册成功调用代码:
/* 注册远程推送通知成功会调用 ,在此接收设备令牌deviceToken */
- (void)application:(UIApplication *)application 
        didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    [self addDeviceToken:deviceToken];
}
/* 保存deviceToken,并同步服务器上保存的deviceToken,以便能正确推送通知 */
- (void)addDeviceToken:(NSData *)deviceToken
{
    NSString *key = @"DeviceToken";
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    //取出原来的deviceToken,进行比较
    NSData *oldToken = [defaults objectForKey:key];
    if ([oldToken isEqualToData:deviceToken]) {
        //存入新的deviceToken
        [defaults setObject:deviceToken forKey:key];
        [defaults synchronize];
        //发送网络请求到服务器,说明deviceToken发生了改变
        [self sendDeviceTokenWithOldDeviceToken:oldToken newDeviceToken:deviceToken];
    }
}
/* 发送网络请求到服务器,说明deviceToken发生了改变,服务器那边也要同步改变 */
- (void)sendDeviceTokenWithOldDeviceToken:(NSData *)oldToken newDeviceToken:(NSData *)newToken
{
    //发送到服务器,下面是服务器的一个接口
    NSString *urlStr = @"http://192.168.1.101/RegisterDeviceToken.aspx";
    urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url = [NSURL URLWithString:urlStr];
    //POST网络请求
    NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:url];
    requestM.HTTPMethod = @"POST";
    //POST请求的请求体
    NSString *bodyStr = [NSString stringWithFormat:@"oldToken=%@&newToken=%@",oldToken,newToken];
    requestM.HTTPBody = [bodyStr dataUsingEncoding:NSUTF8StringEncoding];
    //使用会话来发送网络请求
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = 
          [session dataTaskWithRequest:requestM 
                     completionHandler:^(NSData *data,NSURLResponse *response,NSError *error){
        if(!error){
            NSLog(@"Send Success !");
        } else {
            NSLog(@"Send Failure, error = %@",error.localizedDescription);
        }
    }];
    //网络请求任务启动
    [dataTask resume];
}
3. 监听远程推送通知:
/* 收到远程推送通知时会调用 */
- (void)application:(UIApplication *)application 
        didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    NSString *title = @"远程推送通知";
    NSString *msg = userInfo[@"msg"];
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title 
                                                    message:msg 
                                                   delegate:nil 
                                          cancelButtonTitle:@"取消" 
                                          otherButtonTitles:@"确定", nil];
    [alert show];
}

四、第三方远程推送

上面的远程推送过程如果觉得实现比较麻烦,你可以使用第三方推送,例如:

  • 极光推送( JPush ),我只用过这个,界面还不错,这不是在打广告!
  • 个推
  • 腾讯信鸽

具体的集成步骤及使用方法,请查看对应的官方文档,非常详细。

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

推荐阅读更多精彩内容

  • 极光推送: 1.JPush当前版本是1.8.2,其SDK的开发除了正常的功能完善和扩展外也紧随苹果官方的步伐,SD...
    Isspace阅读 6,600评论 10 16
  • 推送通知注意:这里说的推送通知跟NSNotification有所区别NSNotification是抽象的,不可见的...
    醉叶惜秋阅读 1,468评论 0 3
  • 推送通知 注意:这里说的推送通知跟NSNotification有所区别 NSNotification是抽象的,不可...
    iOS开发攻城狮阅读 4,087评论 1 13
  • 概述 在多数移动应用中任何时候都只能有一个应用程序处于活跃状态,如果其他应用此刻发生了一些用户感兴趣的那么通过通知...
    莫离_焱阅读 6,391评论 1 8
  • 翅荚决明 (学名:Cassia alata)为豆科决明属下的一个种。 分布于广东和云南南部地区。生于疏林或较干旱的...
    王不懂Sir阅读 326评论 0 0