包教包会:本地推送 & 远程推送

什么是推送?注意,和我们常用的抽象通知不同(NSNotification): 可以让不在前台运行的app,告知用户app内部发生了什么事情;或者没有运行的app接收到服务器发来的通知。。比如离线QQ接受消息,网上商城的打折通知,游戏的版本更新通知,有新的评论生成。。。。
 
iOS的推送分为:本地推送和远程推送。本地推送是应用内的推送,不用连接服务器,常用来定时提醒用户完成一些任务,比如清理垃圾、记账、买衣服、看电影、玩游戏。远程推送则是通过服务器返回实时的通知信息给用户。

先回忆一下,推送通知有5种不同的呈现效果(另外还会在 “通知中心” 进行展示,就是顶端下拉屏幕后的视图):

1、在屏幕顶部显示一块横幅(显示具体内容)

2、在屏幕中间弹出一个UIAlertView(显示具体内容)   很少很少使用这种,,用户体验很差

3、在锁屏界面显示一块横幅(锁屏状态下,显示具体内容)

4、更新app图标的数字(说明新内容的数量)

5、播放音效(提醒作用)

 

我们开始正式配置本地和远程两种推送,并且着重从本地推送开始,因为远程推送的代码实现,需要本地推送的基础~~~~

 

一、如何发出本地推送通知:三步走 + 注册通知(iOS8.0之后才需要,所以小心坑,要做iOS7的适配)

 

1、创建本地推送通知对象

UILocalNotification *ln = [[UILocalNotification alloc] init];

 

2、设置本地推送通知属性

 

(1)推送通知的触发时间(何时发出推送通知)

@property(nonatomic,copy) NSDate *fireDate;

 

(2)推送通知的具体内容

@property(nonatomic,copy) NSString *alertBody;

 

(3)在锁屏时显示的动作标题(完整标题:“滑动来” + alertAction)

@property(nonatomic,copy) NSString *alertAction;

 

(4)音效文件名

@property(nonatomic,copy) NSString *soundName;

 

(5)app图标数字

@property(nonatomic) NSInteger applicationIconBadgeNumber;

 

(6)每隔多久重复发一次推送通知

@property(nonatomic) NSCalendarUnit repeatInterval;

 

(7)点击推送通知打开app时显示的启动图片,不设置则没有,如果设置(无论设置成什么),只会显示app启动的图片,很坑的一个属性

@property(nonatomic,copy) NSString *alertLaunchImage;

 

(8)附加的额外信息

@property(nonatomic,copy) NSDictionary *userInfo;

 

(9)时区

@property(nonatomic,copy) NSTimeZone *timeZone;

(一般设置为[NSTimeZone defaultTimeZone] ,跟随手机的时区)

 

(10)在通知中心里显示的标题

@property(nonatomic,copy) NSString *title;

 

(11)显示滑动解锁的按钮:设置成no,则显示“滑动来查看”,不显示用户设定的内容

@property(nonatomic) BOOL *hasAction;

 

 

3、推送通知的发出

 

(1)调度本地推送通知(调度完毕后,推送通知会在特地时间fireDate发出)

[[UIApplication sharedApplication] scheduleLocalNotification:ln];

 

(2)获得被调度(定制)的所有本地推送通知

@property(nonatomic,copy) NSArray *scheduledLocalNotifications;

(已经发出且过期的推送通知就算调度结束,会自动从这个数组中移除)

 

(3)取消调度本地推送通知

- (void)cancelLocalNotification:(UILocalNotification *)notification;

- (void)cancelAllLocalNotifications;

 

(4)立即发出本地推送通知

- (void)presentLocalNotificationNow:(UILocalNotification *)notification;

 

 

 

二、点击本地推送通知的响应

正常情况点击弹出的通知,会打开应用程序,所以,这里的响应,指的是“即将进入前台”

1、当用户点击本地推送通知,会自动打开app,这里有3种情况

(1) 状态:app并没有关闭,一直隐藏在后台;

     操作:让app进入前台,并会调用AppDelegate的下面方法(并非重新启动app,而是即将进入前台的响应  

 - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;

 

(2) 状态:app在前台运行;

     操作:推送通知仍然会在发送,也会走下面的方法,虽然没有跳转到app  

 - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;

 

(3) 状态:app已经被关闭(进程已死)

         操作:启动app,启动完毕会调用AppDelegate的下面方法,不会调用上面的方法

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

  launchOptions参数通过UIApplicationLaunchOptionsLocalNotificationKey取出本地推送通知对象

 

 

2、关于页面跳转的逻辑判断(要进行程序所处状态的逻辑判断,,哎呀,如果屡不清看下面代码。。)

 

(1)程序在前台运行时,不需要根据本地推送进行跳转操作

(2)程序在后台运行时,跳转到通知逻辑指定的页面 

(3)程序关闭时,跳转到通知逻辑指定的页面

(4)程序第一次运行(本地推送还没有发生),是不需要加载推送通知逻辑的

 

 3、要结合app的生命周期方法和状态值,来进行上面业务逻辑的判断

(1)Not running   未运⾏,程序没启动。
(2)Inactive     未激活,程序在前台运⾏,不过没有接收到事件。在没有事件处理情况下程序通常停留在这个状态。
(3)Active      激活,程序在前台运行而且接收到了事件。这也是前台的一个正常的模式。
(4)Backgroud     后台,程序在后台而且能执⾏代码,大多数程序进⼊这个状态后会在这个状态上停留⼀ 会儿。

(5)Suspended     上一状态时间到了之后会进入挂起状态。有的程序经过特殊的请求后可以长期处于Backgroud状态,Suspended 挂起程序在后台不能执⾏代码。系统会自动把程序变成这个状态而且不会发出通知。当挂起时,程序还是停留在内存中的,当系统内存低时,系统就把挂起的程序清除掉,为前台程序提供更多的内存。

 

4、给出一个参考示例: 

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    // 设置应用程序的图标右上角的数字
    [application setApplicationIconBadgeNumber:0];
    
  //适配 iOS7.0,大于7.0的版本再去注册通知 if ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0) { UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil]; [application registerUserNotificationSettings:settings]; } // 界面的跳转(针对应用程序被杀死的状态下的跳转,这里判断键值是否存在,如果存在,说明是接受到通知之后开启的app,那么就执行跳转) if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) { // 跳转代码(只是简单的示范,redview中会打印出字典的内容,用来查看接受到通知携带的内容) UILabel *redView = [[UILabel alloc] init]; redView.frame = CGRectMake(0, 0, 200, 300); redView.numberOfLines = 0; redView.font = [UIFont systemFontOfSize:12.0]; redView.backgroundColor = [UIColor redColor]; redView.text = [NSString stringWithFormat:@"%@", launchOptions]; [self.window.rootViewController.view addSubview:redView]; } return YES; } /* 点击通知,应用程序正在进入前台;或者在前台的时候都会执行该方法 */ - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { // 针对应用程序在后台的时候进行的跳转,如果程序在前台不用跳转
  //注意此时的应用程序状态! if (application.applicationState == UIApplicationStateInactive) { NSLog(@"进行界面的跳转"); NSLog(@"%@", notification.userInfo); UIView *redView = [[UIView alloc] init]; redView.frame = CGRectMake(0, 0, 100, 100); redView.backgroundColor = [UIColor redColor]; [self.window.rootViewController.view addSubview:redView]; } }

 

三、关于注册通知(获得许可)的补充介绍

 

1、从iOS 8.0开始,如果要使用通知,需要得到用户的许可,也就是弹出一个提示框,询问用户是否授权。这个选择也可以在手机的“设置”中进行配置(包括推送通知显示的位置也可以选择)。

 

2、设置方式

 

(1)设置setting,做一些配置,包括推送通知可以显示的类型

   UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:nil];

 

(2)应用程序对象注册推送

   [[UIApplication sharedApplication] registerUserNotificationSettings:settings];

 

(3)关于分类categories

   通过分类可以给推送通知添加快捷回复,包括按钮和文本框,比如QQ的快捷回复,微博的快捷点赞等功能。

 

 

四、单独说一下 category的设置

 

1、在通知处显示按钮,可以点击进行快捷回复,默认会回到程序中。另外可以设置快捷回复的文本框,进行信息发送。这些按钮是可以设置响应事件,进行逻辑实现的!

   

2、设置

 

(1)配置可变的category对象

   UIMutableUserNotificationCategory *category = [[UIMutableUserNotificationCategory alloc]init];

    

(2)设置标识符,这个必须有,通知对象根据这个标识,从注册的category中找到指定的,设置自己的category属性

   category.identifier = @"cate";

    

(3)为category 准备 action(动作按钮),这点类似  alertViewController

 

 UIMutableUserNotificationAction*action = [[UIMutableUserNotificationAction alloc]init];

    action.title = @“已阅读”;

    action.identifier = @"action1”;

 //让按钮的响应在后台处理,不用跳转到应用程序中去

    action.activationMode = UIUserNotificationActivationModeBackground;

//true时,用户在点击动作之前必须确认自己的身份

    action.authenticationRequired = NO;

 //在通知中心显示不同背景颜色

    action.destructive = NO;

 

     

(4)设置快捷回复的文本框,点击按钮出现输入文本框进行快捷回复

   action.behavior = UIUserNotificationActionBehaviorTextInput;

    

(5)添加动作到分类

    [category setActions:@[action1,action2] forContext:UIUserNotificationActionContextDefault];

 

(6)在注册通知的时候,添加分类进去

    UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound |  UIUserNotificationTypeAlert  categories:[NSSet setWithObjects:category, nil]];

     [[UIApplication sharedApplication]registerUserNotificationSettings:setting];

 

3、动作按钮的响应(application的代理方法),在这里面进行业务逻辑的实现

 

(1)动作按钮的响应事件————不带文本框信息

 

- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler

    if ([identifier isEqualToString:@"action1”]) { //点击了标记已读
        NSLog(@"点击了标记已读");
    }
    if ([identifier isEqualToString:@"action2”]) { //点击了删除
        NSLog(@"点击了删除");
    }

//这里执行一下block
    completionHandler();
}

 

 

(2)动作按钮的响应事件————快捷回复带文本框信息(和上面的方法不要一起写)

 

- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
{
    if ([identifier isEqualToString:@"action2”]) { //点击了标记已读
//注意key
        NSLog(@"%@",responseInfo[UIUserNotificationActionResponseTypedTextKey]);
    } 
    completionHandler();

}

 

 

 

五、远程推送的原理

 

1、苹果的服务器会和所有的iPhone建立长连接(不懂长连接去找度娘),所以,苹果可以通过这个长连接,实现对所有用iPhone手机的时间校准、系统升级、查找位置等等操作(废话,因为一直和手机连着网么。。。),因而,服务器也可以实现给所有用户发送通知消息,然后用户手机接收到后,针对不同的应用,实现类似本地推送的展示效果。  然而苹果的服务器怎么知道发送什么东西呢----->我们的app后台服务器告诉他的;但是我们的后台服务器以及苹果的服务器又是怎么确定发送给哪个手机和应用的呢----->通过标识deviceToken。。。最后苹果的这个“服务器”,准确的说是服务,叫做 : APNs  (Apple Push Notification Service)。

 

2、远程推送流程详解

   deviceToken:就是由UDID 和 bundleID 生成的唯一标识符

     

 

  (1)  设备(应用)向苹果服务器请求获得deviceToken

    (2)苹果根据请求信息,生成deviceToken,并将其返回给设备

    (3)设备把收到的deviceToken发送给应用的后台服务器,这时服务器记录下了deviceToken 

  (4)应用的服务器需要发送推送时,附加这deviceToken发送给APNs(苹果服务器)

    (5)苹果接受到推送后,根据deviceToken,把信息传达给和他保持长连接的iPhone设备

    (6)设备监听并接受到远程推送,执行类似本地推送的展示

 

 

 

六、远程推送实现的准备工作

 

1、开发iOS程序的推送功能, iOS端需要做的事

  (1)  应用向苹果服务器请求获得deviceToken

    (2)得到苹果返回的deviceToken

    (3)发送deviceToken给应用的后台服务器,这时(自己的)服务器记录下了deviceToken

  (4)监听用户对通知的点击

  

2、调试iOS的远程推送功能, 必备条件:

 

(1)真机,模拟器玩不了咯

 

(2)调试推送需要的证书文件:悲剧的你,需要申请开发者账号了,,不贵,个人账号也就688元

  1> development.cer :  调试推送证书 。   某台电脑就能调试某个app的推送服务

  2> mobileprovision : 描述文件。            某台电脑就能利用某台设备调试某个程序(关联上了设备)

   

  补充一下:证书的类型(没有开发经验的弟弟们经常搞不清楚的东西),描述文件是根据证书和注册设备生成的

       

  

(3)发布具有推送服务的app

  1> production.cer : 如果发布的程序中包含了推送服务,就必须安装这个证书 

  2> mobileprovision  : 某台电脑就能发布某个程序

 

3、远程推送准备的流程概述

(1)登录开发者账号,创建App ID:说明哪个App使用推送,绑定bundleID,注意AppID是全称,不能带*(通配的模式不行)

(2)为App ID创建APNs SSL证书:就是上面说的推送证书,这里要关联上面的AppID

(3)生成描述文件:根据上面两个东西生成的--> 最终确定 “哪台设备要在哪台电脑上调试哪个程序”

(4)把证书和描述文件下载安装了:双击,最好先装cer

 

七、开始代码配置~~

上面的工作是远程推送的准备工作,设置完毕后,我们就可以进行代码实现了,很多代码逻辑和本地推送类似

 

1、注册通知,请求deviceToken(只要注册了,就自动发出请求,但是要注意ios版本问题!)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    if ([[UIDevice currentDevice].systemVersion doubleValue] >= 8.0) { //iOS8
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
        [application registerUserNotificationSettings:settings];
        [application registerForRemoteNotifications];
    }

     else { // 适配iOS7
        [application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeNewsstandContentAvailability | UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert];
    }
    
  //这里类似本地推送的逻辑实现,要判断是否需要跳转 if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) { // 跳转 // 添加一个红色的View } return YES; }

 

2、获得deviceToken

 

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    // 业务逻辑:将DeviceToken传给服务器
    NSLog(@"%@", deviceToken.description);
}

  

3、假设服务器开始发送推送了,接收到推送

(1)服务器发过来的信息有固定格式(userInfo)类比本地推送对象的属性通过这些格式可以确定--> 声音、badge、额外的字段、推送信息等内容

(2)可以使用pushMeBaby这个小程序模拟服务器发送推送~~

(3)这个方法就是类似本地推送时获得推送的响应方法,注意判断是否跳转。

 

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
    NSLog(@"%@", userInfo);      
}

 

 

4、还有一种接受推送的方式:后台处理

(1)应用程序接收到推送之后,就立即响应,用户不用操作(不用点击通知),在“静默”的情况下,实现了推送想实现的逻辑

(2)推送信息(userInfo)中必须包含字段:"content-available":"1",其中 “1”随便写

(3)实现下面的代码,注意里面有回调block,需要告诉系统是否有新内容(枚举常量)

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    NSLog(@"虽然用户没有察觉,但是已经接受到通知");
    
    UIView *redView = [[UIView alloc] init];
    redView.backgroundColor = [UIColor redColor];
    redView.frame = CGRectMake(100, 100, 100, 100);
    [self.window.rootViewController.view addSubview:redView];
    
    // 1.打开后台模式 2.告诉系统是否有新内容的更新 3.发送的通知有固定的格式("content-available":"1")
    completionHandler(UIBackgroundFetchResultNewData);
}

 

(3)还要有额外的配置,来实现后台运行

   

 

 

 

八、总结

好了,推送的东西讲完了,关于如何申请开发者账号、申请调试证书、注册设备、生成描述文件之类的事情,大家就靠度娘吧,其实也很简单,记住上文中的证书类型页面就好,就那么几种。

另外,苹果的服务器有时候会抽风。。。deviceToken不能及时获取,大家多试几遍,或者找较好的网络环境试试。。

后面有机会给大家介绍第三方的推送,,那个才是真正的应用(貌似白写了这么多。。。)

 

posted @ 2016-04-26 23:10  执着的怪味豆  阅读(624)  评论(2编辑  收藏  举报