一、简介
分为本地推送和远程推送2种。可以在应用没打开甚至手机锁屏情况下给用户以提示。它们都需要注册,注册后系统会弹出提示框(如下图)提示用户石否同意,如果同意则正常使用;如果用户不同意则下次打开程序也不会弹出改提示框,需要用户到设置里面设置。一共有三种提示类型:
UIUserNotificationTypeBadge:应用图标右上角的信息提示
UIUserNotificationTypeSound:播放提示音
UIUserNotificationTypeAlert:提示框
二、本地推送
1.注册与处理
代码如下:
/// 一般在在启动时注册通知,程序被杀死,点击通知后调用此程序- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
- (void)registerAPN {
// 注册通知
if (@available(iOS 10.0, *)) { // iOS10 以上
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
}];
} else {// iOS8.0 以上
UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:setting];
}
}
return YES; } /// 程序没有被杀死(处于前台或后台),点击通知后会调用此程序 - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { // 这里添加处理代码 }
可以看到,处理代码有两个方法,一个是
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
如果程序没有被杀死,即处于前台或者后台,那么调用前者;如果程序被杀死,则调用后者。
- (void)applicationWillResignActive:(UIApplication *)application {
#pragma mark -->NSLog(@"\n ===> 程序挂起 !"); 比如:当有电话进来或者锁屏,这时你的应用程会挂起,在这时,UIApplicationDelegate委托会收到通知,调用 applicationWillResignActive 方法,你可以重写这个方法,做挂起前的工作,比如关闭网络,保存数据。
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
#pragma mark --> NSLog(@"\n ===> 程序进入后台 !");
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
#pragma mark --> NSLog(@"\n ===> 程序进入前台 !");
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
#pragma mark --> NSLog(@"\n ===> 程序重新激活 !"); 应用程序在启动时,在调用了 applicationDidFinishLaunching 方法之后也会调用 applicationDidBecomeActive 方法,所以你要确保你的代码能够分清复原与启动,避免出现逻辑上的bug。(大白话就是说:只要启动app就会走此方法)。
}
- (void)applicationWillTerminate:(UIApplication *)application {
#pragma mark --> 当用户按下按钮,或者关机,程序都会被终止。当一个程序将要正常终止时会调用 applicationWillTerminate 方法。但是如果长主按钮强制退出,则不会调用该方法。这个方法该执行剩下的清理工作,比如所有的连接都能正常关闭,并在程序退出前执行任何其他的必要的工作.
}
2.发送通知
- (void)addLocalNotice {
if (@available(iOS 10.0, *)) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
// 标题
content.title = @"测试标题";
content.subtitle = @"测试通知副标题";
// 内容
content.body = @"测试通知的具体内容";
// 声音
// content.sound = [UNNotificationSound defaultSound];
content.sound = [UNNotificationSound soundNamed:@"Alert_ActivityGoalAttained_Salient_Haptic.caf"];
// 角标 (我这里测试的角标无效,暂时没找到原因)
content.badge = @1;
// 多少秒后发送,可以将固定的日期转化为时间
NSTimeInterval time = [[NSDate dateWithTimeIntervalSinceNow:2] timeIntervalSinceNow];
// NSTimeInterval time = 10;
// repeats,是否重复,如果重复的话时间必须大于60s,要不会报错
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:time repeats:NO];
/*
//如果想重复可以使用这个,按日期
// 周一早上 8:00 上班
NSDateComponents *components = [[NSDateComponents alloc] init];
// 注意,weekday默认是从周日开始
components.weekday = 2;
components.hour = 8;
UNCalendarNotificationTrigger *calendarTrigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:YES];
*/
// 添加通知的标识符,可以用于移除,更新等操作
NSString *identifier = @"noticeId";
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];
[center addNotificationRequest:request withCompletionHandler:^(NSError *_Nullable error) {
NSLog(@"成功添加推送");
}];
}else {
UILocalNotification *notif = [[UILocalNotification alloc] init];
// 发出推送的日期
notif.fireDate = [NSDate dateWithTimeIntervalSinceNow:5];
// 推送的内容
notif.alertBody = @"你已经10秒没出现了";
// 可以添加特定信息
notif.userInfo = @{@"noticeId":@"00001"};
// 角标
notif.applicationIconBadgeNumber = 1;
// 提示音
notif.soundName = UILocalNotificationDefaultSoundName;
// 每周循环提醒
notif.repeatInterval = NSCalendarUnitWeekOfYear;
[[UIApplication sharedApplication] scheduleLocalNotification:notif];
}
}
3. 取消通知
// 取消所有本地通知[application cancelAllLocalNotifications]; 三、远程推送
与Android上我们自己实现的推送服务不一样,Apple对设备的控制非常严格,消息推送的流程必须要经过APNs(Apple Push Notification service).
一般情况下如果一个程序退到后台就不能运行代码(Audio、VoIP等等可以在后台运行),或者程序退出后,那么它就和对应应用的后台服务器断开了链接,就收不到服务器发送的信息了,但是每台设备只要联网就会和苹果的APNs服务器建立一个长连接(persistent IP connection),这样只要通过苹果的APNs服务器,我们自己的服务器就可以间接的和设备保持连接了,示意图如下:
使用步骤:
1 勾选Backgroud Modes -> Remote notifications,主要是iOS7之后,苹果支持后台运行,如果这里打开后,当接收到远程推送后,程序在后台也可以做一些处理,如下图所示:
2.远程推送机制
你的应用服务端将消息发送到apple的APNS服务器,APNS服务器将消息推送到指定的Iphone,最后由Iphone负责将消息推送至你的APP。在此先不说这个过程是如何实现的,仅仅看这个流程,你可能会觉得,在你们服务端和客户端之间增加了一个apple的APNS,不是增加开发者的负担么?其实结果恰恰相反,因为apple对推送的统一管理,使我们开发者的工作变得异常简单。
3.服务端如何连接到客户端的
如果你是做android开发的,你一定非常了解长链接与心跳包。事实上,大部分的android应用的推送也确实是通过长链接来实现的。因为android系统的开放性,APP是很容易做到自启动和后台长链接的,而心跳验证,就是始终保证长链接属于接通状态,然后由服务端直接推送消息。如果IOS开发者也采用这种思路,就十分困难了,在IOS中想要保持一个APP服务始终不被系统杀死,我只能说太难了。通过上面的流程图,对比android的推送思路,我们很容易明白,IOS中其实也始终有一个长链接,那就是系统本身,这个长链接始终与APNS服务器相连,然后统一管理所有应用程序的推送。 4.这是IOS推送机制的优势?
1、因为推送的服务端是appleID的验证用户,推送可靠性会高。
2、所有推送消息由APNS统一管理,效率高。
3、在客户端只需系统维护一个长链接,节省了用户流量消耗和手机的性能消耗,并且提高了安全性,使得有恶意推送和流氓软件的几率降低。
准备工作:参考大神博客