推送通知
推送通知跟NSNotification有所区别:
1> NSNotification是抽象的,不可见的
2> 推送通知是可见的(能用肉眼看到)
iOS中提供了2种推送通知: 本地推送通知, 远程推送通知
1> 本地推送通知(Local Notification)
2> 远程推送通知(Remote Notification)
推送通知可以不让在前台运行的app,告知app内部发生了什么变化,比如:有新的内容,新消息等.
推送通知的使用:
发出推送通知时,如果当前程序正运行在前台,那么推送通知就不会被呈现出来
点击推送通知后,默认会自动打开发出推送通知的app
不管app打开还是关闭,推送通知都能如期发出
推送通知有5种呈现效果:
1. 在屏幕顶部显示一块横幅(显示具体内容)
2. 在屏幕中间弹出一个UIAlertView(显示具体内容,使用较少)
3. 在锁屏界面显示一块横幅(锁屏状态下,显示具体内容)
4. 播放音效(提醒作用)
5. 更新app图标的数字(说明新内容的数量)
推送通知分为本地推送通知和远程推送通知,下面一一介绍.
本地推送通知
本地推送通知不需要服务器的支持,不需要联网就可以发送通知.通常本地推送通知用于定时提醒用户,比如清理垃圾,淘宝购物,纪念日提醒等任务.
在苹果官方给出了本地推送通知的一些属性,以及使用.
属性介绍:
@property(nonatomic,copy) NSDate *fireDate; // 设置本地推送的时间
@property(nonatomic,copy) NSTimeZone *timeZone; // 时区(一般设置为[NSTimeZone defaultTimeZone] ,跟随手机的时区)
@property(nonatomic) NSCalendarUnit repeatInterval; // 没隔多久重复发出一次
@property(nonatomic,copy) NSCalendar *repeatCalendar; // 设置日期
@property(nonatomic,copy) CLRegion *region NS_AVAILABLE_IOS(8_0); // 比如某一个区域的时候发出通知
@property(nonatomic,assign) BOOL regionTriggersOnce NS_AVAILABLE_IOS(8_0); // 进入区域是否重复
@property(nonatomic,copy) NSDictionary *userInfo; // 附加的额外信息
@property(nonatomic,copy) NSString *alertBody; // 消息的内容
@property(nonatomic) BOOL hasAction; // 是否显示alertAction的文字(默认是YES)
@property(nonatomic,copy) NSString *alertAction; // 设置锁屏状态下,显示的一个文字
@property(nonatomic,copy) NSString *alertLaunchImage; // 启动图片
@property(nonatomic,copy) NSString *soundName; // UILocalNotificationDefaultSoundName
@property(nonatomic) NSInteger applicationIconBadgeNumber; // 应用图标右上角的提醒数字
@property(nonatomic,copy) NSArray *scheduledLocalNotifications; // 获得被调度(定制)的所有本地推送通知(已经发出且过期的推送通知就算调度结束,会自动从这个数组中移除)
使用方法:
创建本地通知
UILocalNotification *localNoti = [[UILocalNotification alloc] init];
调度本地推送通知(调度完毕后,推送通知会在特地时间fireDate发出)
[[UIApplication sharedApplication] scheduleLocalNotification:localNoti];
取消调度本地推送通知
- (void)cancelLocalNotification:(UILocalNotification *)notification;
- (void)cancelAllLocalNotifications;
- 立即发出本地推送通知
- (void)presentLocalNotificationNow:(UILocalNotification *)notification;
注意:
iOS8.0以后在本地推送通知上新加了一些新功能,为了用户体验,以及更加人性化,如果要使用本地通知,需要得到用户的许可.
需要在AppDelegate中添加如下代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/*
UIUserNotificationTypeNone = 0, 没有,没有本地通知
UIUserNotificationTypeBadge = 1 << 0, 接受图标右上角提醒数字
UIUserNotificationTypeSound = 1 << 1, 接受通知时候,可以发出音效
UIUserNotificationTypeAlert = 1 << 2, 接受提醒(横幅/弹窗)
*/
// iOS8需要添加请求用户的授权
if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];
[application registerUserNotificationSettings:settings];
}
}
点击本地推送通知
当用户点击本地推送通知,会自动打开app,这里有2种情况
1> app并没有关闭,一直隐藏在后台(运行在后台)
让app进入前台,并会调用AppDelegate的下面方法(并非重新启动app)
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;
- 1
- 1
2> app已经被关闭(进程已死)
启动app,启动完毕会调用AppDelegate的下面方法
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
launchOptions参数通过UIApplicationLaunchOptionsLocalNotificationKey取出本地推送通知对象
若要实现界面的跳转,需要分清当前应用程序的所处状态,进行判断:
1> 若是当前应用在后台运行,接收到通知时,要想进行界面的跳转,可以在didReceiveLocalNotification:方法中实现跳转界面的方法
2> 若是当前的应用程序已经关闭,我们在前面说到,当应用关闭,推送通知也会如期发送.但此时,是不会走didReceiveLocalNotification:方法的,那我们只有didFinishLaunchingWithOptions:方法利用,launchOptions参数通过UIApplicationLaunchOptionsLocalNotificationKey取出本地推送通知对象,实现跳转的功能.
下面是演示代码:
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
/*
UIUserNotificationTypeNone = 0, 没有,没有本地通知
UIUserNotificationTypeBadge = 1 << 0, 接受图标右上角提醒数字
UIUserNotificationTypeSound = 1 << 1, 接受通知时候,可以发出音效
UIUserNotificationTypeAlert = 1 << 2, 接受提醒(横幅/弹窗)
*/
// iOS8需要添加请求用户的授权
if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];
[application registerUserNotificationSettings:settings];
}
if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) {
// 跳转界面
}
return YES;
}
/**
* 如果应用在后台,通过点击通知的时候打开应用会来到该代理方法
* 如果应用在前台,接受到本地通知就会调用该方法
*
* @param notification 通过哪一个通知来这里
*/
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
if (application.applicationState == UIApplicationStateActive) return;
if (application.applicationState == UIApplicationStateInactive) {
// 实现跳转
}
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return YES;
}
#import "ViewController.h"
@interface ViewController ()
// 点击按钮之后添加通知
- (IBAction)addLocalNote;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
}
- (IBAction)addLocalNote {
// 1.创建本地通知
UILocalNotification *localNote = [[UILocalNotification alloc] init];
// 设置什么时间弹出
localNote.fireDate = [NSDate dateWithTimeIntervalSinceNow:5];
// 设置弹出的内容
localNote.alertBody = @"您有新消息";
// 设置锁屏状态下,显示的一个文字
localNote.alertAction = @"快点打开";
// 是否显示alertAction的文字(默认是YES)
localNote.hasAction = YES;
// 设置音效
localNote.soundName = UILocalNotificationDefaultSoundName;
// 应用图标右上角的提醒数字
localNote.applicationIconBadgeNumber = 1;
// 设置UserInfo来传递信息
localNote.userInfo = @{@"alertBody" : localNote.alertBody, @"applicationIconBadgeNumber" : @(localNote.applicationIconBadgeNumber)};
// 2.调度通知
[[UIApplication sharedApplication] scheduleLocalNotification:localNote];
}
远程推送通知
远程推送通知:就是通过网络从远程服务器推送给客户端的通知.
为什么需要远程推送通知?
传统获取数据的局限性
只要用户关闭了app,就无法跟app的服务器沟通,无法从服务器上获得最新的数据内容
远程推送通知可以解决以上问题
不管用户打开还是关闭app,只要联网了,都能接收到服务器推送的远程通知
远程推送通知的使用
所有的苹果设备,在联网状态下,都会与苹果的服务器建立长连接
长连接: 只要联网了,就一直建立连接
长连接的作用: 时间校准, 系统升级, 查找我的iPhone
长连接的好处 : 数据传输速度快 , 数据保持最新状态
远程推送功能机制
苹果给iOS和Mac添加了消息推送的功能,使得我们可以通过后台服务器给应用程序(APP)发送消息,不管APP是否正在使用,比如邮箱的来件提示功能。这项服务被称为Apple Push Notification service(APNs)。里面一共涉及到四个角色:APP、设备、APNs和应用后台服务器(Provider),其中APP、后台服务器和APNs之间使用deviceToken唯一的标识一个用户。
推送服务的工作流程:
APP向系统注册推送服务。
设备从APNs请求deviceToken。
通过代理方法将deviceToken返回给APP。
APP将deviceToken发送给应用后台服务器(Provider)。
应用后台服务器保存deviceToken,然后在需要推送通知的时候,给APNs发送信息,使用deviceToken标识所要送达的客户端。
APNs将后台服务器发过来的数据推送到设备。
设备将消息分发给应用程序。
在使用推送功能的时候,需要在开发者中心创建支持Push Notification的证书,并且将证书和私钥用于应用后台服务器与APNs之间通信。
我也写了一个比较易懂的远程推送流程图! http://blog.csdn.net/ismilesky/article/details/48324723
远程推送近年来,都是通过第三方进行实现,因为第三方推送的功能比较强大.
推送平台: 百度推送 , 极光推送, 腾讯信鸽推送, 个推
远程推送我们这里以极光推送(第三方)为例,进行测试.
极光远程推送的使用
如要使用极光第三方远程推送,我们需要集成iOS SDK,需要进行应用的配置,和环境配置.在开发者中心有
developer.apple.com开发者账号 , iOS真机(iPhone、iPad、iPod)等。
远程推送应用配置过程:
- 创建支持远程推送功能的App ID
- 申请开发者证书,并选中刚刚创建的App ID
- 下载CER文件,并导入钥匙串管理
- 申请发布证书,并选中刚刚创建的App ID
- 下载CER文件,并导入钥匙串管理
- 检查App ID,确认证书已经指定
这些相关配置极光推送已经给了非常详细的文档资料, iOS SDK集成指南 :http://docs.jpush.io/guideline/ios_guide/ , iOS SDK调试指南 :http://docs.jpush.io/client/ios_tutorials/#ios-sdk, 只需要按照流程进行就可以.
实现代码:
#define kDeviceVersion ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0)
#import "AppDelegate.h"
#import "APService.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_7_1
if (kDeviceVersion) {
//可以添加自定义categories
[APService registerForRemoteNotificationTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound |UIUserNotificationTypeAlert) categories:nil];
} else {
//categories 必须为nil
[APService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert) categories:nil];
}
#else
//categories 必须为nil
[APService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert) categories:nil];
#endif
[APService setupWithOption:launchOptions];
return YES;
}
#pragma mark - 获取device token (必须实现)
// 当得到苹果的APNs服务器返回的DeviceToken就会被调用
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSLog(@"%@",deviceToken);
// Required
[APService registerDeviceToken:deviceToken];
}
#pragma mark - 获取device token失败
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
NSLog(@"function == %s line == %d error == %@",__FUNCTION__,__LINE__,error);
}
// 接收到远程通知,触发方法和本地通知一致 (必须实现)
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
// Required
[APService handleRemoteNotification:userInfo];
}
#pragma mark - 使用后台的远程消息推送 (必须实现)
/**
1> 在Capabilities中打开远程推送通知
2> 实现该代理方法
远程消息数据格式:
{"aps" : {"content-available" : 1},"content-id" : 42}
执行completionHandler有两个目的
1> 系统会估量App消耗的电量,并根据传递的UIBackgroundFetchResult 参数记录新数据是否可用
2> 调用完成的处理代码时,应用的界面缩略图会自动更新
注意:接收到远程通知到执行完网络请求之间的时间不能超过30秒
if (userInfo) {
int contentId = [userInfo[@"content-id"] intValue];
ViewController *vc = (ViewController *)application.keyWindow.rootViewController;
[vc loadDataWithContentID:contentId completion:^(NSArray *dataList) {
vc.dataList = dataList;
NSLog(@"刷新数据结束");
completionHandler(UIBackgroundFetchResultNewData);
}];
} else {
completionHandler(UIBackgroundFetchResultNoData);
}
*/
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// IOS 7 Support Required
[APService handleRemoteNotification:userInfo];
[[UIApplication sharedApplication]setApplicationIconBadgeNumber:0];
// 判断应用程序在前台还是后台
if (application.applicationState == UIApplicationStateActive) { // 活跃状态
// 实现方法
} else if (application.applicationState == UIApplicationStateInactive) {
// 不活跃状态
// 实现方法
} else { // application.applicationState == UIApplicationStateBackground
// 后台
// 实现方法
}
/** 必须回调 */
completionHandler(UIBackgroundFetchResultNewData);
}
测试时,运行完应用程序,要发送通知,进行推送的测试,真机收到通知才算成功.