一、前言
推送分为远程推送和本地推送。
二、远程推送
1.概念
1)所谓远程推送指的是从服务器推送到客户端的通知(需要联网),远程推送又称为APNs
2)远程推送的好处:传统的获取数据方法有局限性(用户只能在App打开时发送数据请求来获取服务器的数据)
3)使用须知:所有的苹果设备,在联网状态下,都会与苹果的服务器保持建立长连接(长连接:数据传输速度快、数据保持最新)
2.推送原理
1)用户在联网状态与苹果的APNS服务器保持着长连接
2)用户在App完成完成后,会向苹果的APNS服务器发送自己的设备ID和App的BoundleID
3)苹果将用户设备ID和用户所安装App的BoundleID加密形成一个deviceToken,并通过AppDelegate方法返回给用户
4)用户安装完App,当第一次打开应用,就会向App服务器发送自已的设备ID和deviceToken
5)App服务器拿到用户ID和deviceToken后,就会将它们存放在App服务器的数据库中
6)当需要发送推送消息时,App服务器就会从数据库中查找对应用户ID,从而找到相应的deviceToken,并且将推送消息与deviceToken一并发送给苹果的APNS服务器
7)苹果APNS服务器通过deviceToken找到相应的用户设备中安装的相应App,将消息发送过去
3.使用远程推送(不使用任何第三方)
1)首先我们必须去Apple Developer开发者中心制作推送证书和推送证书配置文件
2)代码
1 // AppDelegate.m文件
2 #import "AppDelegate.h"
3
4 @interface AppDelegate ()
5
6 @end
7
8 @implementation AppDelegate
9
10
11 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
12
13 // 当用户第一次启动程序时就会获取deviceToken
14 // 只要调用该方法,系统就会自动发送UUID和App的BunleID到苹果的APNs服务器
15 if ([UIDevice currentDevice].systemVersion.doubleValue <= 8.0) {
16 UIRemoteNotificationType type = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert;
17 [application registerForRemoteNotificationTypes:type];
18 } else {
19 UIUserNotificationType type = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
20 UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:type categories:nil];
21 [application registerUserNotificationSettings:settings];
22 // 申请使用通知
23 [application registerForRemoteNotifications];
24 }
25
26 // 取出推送数据
27 NSDictionary *userInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
28 // 判断是否有远程消息推送
29 if (userInfo) {
30 // 如果有需要在这里处理...
31 // 这里专门用来处理程序处于关闭状态时接收到的远程消息
32 // ....
33 }
34
35 return YES;
36 }
37
38 #pragma mark - 获取用户对应当前应用程序的deviceToken时就会调用
39 - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(nonnull NSData *)deviceToken
40 {
41 // d4cf32c1 eceea0a9 ceace1da f1f24833 b2d81673 9d935d11 03553527 908dac12
42 NSLog(@"deviceToken = %@", deviceToken);
43 }
44
45 #pragma mark - iOS7以前,接收到服务器推送过来的消息就会调用
46 // 如果应用程序在后台,只有用户点击了推送消息,才会调用(但是iOS8以后苹果有了多任务,我们可以打开多任务开关)
47 // 如果应用程序在前台,会直接调用
48 // 注意:只有应用程序是打开状态(前台/后台),才会调用该方法
49 // 注意:如果应用程序是关闭状态(死亡),会调用didFinishLaunchingWithOptions,那么这个方法中,推送信息会放在launchOptions参数之中
50 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo
51 {
52 NSLog(@"userInfo = %@", userInfo);
53 // 这个方法与下面的这个方法是一样的,只不过一个是用于iOS8之前,一个是用于配合iOS8之后的多任务
54 #if 0
55 // 创建UILabel
56 static int count = 0;
57 count++;
58 UILabel *label = [[UILabel alloc] init];
59 label.frame = CGRectMake(0, 250, 200, 200);
60 label.numberOfLines = 0;
61 label.textColor = [UIColor whiteColor];
62 label.backgroundColor = [UIColor redColor];
63 label.text = [NSString stringWithFormat:@"%@\n %d", userInfo, count];
64 [self.window.rootViewController.view addSubview:label];
65 #endif
66 }
67
68 #pragma mark - iOS7以后,接收到服务器推送过来的消息就会调用
69 // iOS8以后用这个方法处理后台任务接收到远程通知,可以配合iOS8以后推出的多任务
70 // iOS7以后都会调用
71 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
72 {
73 // UIBackgroundFetchResultNewData // 成功接收到数据
74 // UIBackgroundFetchResultNoData // 没有接收到数据
75 // UIBackgroundFetchResultFailed // 接收失败
76
77 // 取出推送数据
78 NSNumber *contentId = userInfo[@"content-id"];
79 if (contentId) {
80 // 创建UILabel
81 UILabel *label = [[UILabel alloc] init];
82 label.frame = CGRectMake(0, 250, 200, 200);
83 label.numberOfLines = 0;
84 label.textColor = [UIColor whiteColor];
85 label.backgroundColor = [UIColor redColor];
86 label.text = [NSString stringWithFormat:@"\n %@", contentId];
87 [self.window.rootViewController.view addSubview:label];
88
89 // 注意:在此方法中一定要调用这个block,告诉系统是否处理成功,以便于系统在后台中去更新UI等操作
90 completionHandler(UIBackgroundFetchResultNewData);
91
92 } else {
93 // 注意:在此方法中一定要调用这个block,告诉系统是否处理成功,以便于系统在后台中去更新UI等操作
94 completionHandler(UIBackgroundFetchResultFailed);
95 }
96 }
97
98 @end
4.JPush(极光推送)
1)登录注册极光推送的官方网站
2)按照JPush推送的iOS教程一步步来
这里只是提供制作证书的步骤:
3)代码集成
1 // AppDelegate.m文件
2 #import "AppDelegate.h"
3 #import "JPUSHService.h"
4
5 @interface AppDelegate ()
6
7 @end
8
9 @implementation AppDelegate
10
11
12 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
13 {
14
15 if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
16 // 可以添加自定义categories
17 [JPUSHService registerForRemoteNotificationTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert) categories:nil];
18
19 } else {
20 // categories 必须为nil
21 [JPUSHService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert) categories:nil];
22 }
23
24 // 启动极光推送的SDK
25 // 参数1:launchingOption 启动参数
26 // 参数2:appKey 应用必须的唯一标识(参考JPush相关文档获取)
27 // 参数3:channel 发布渠道(可选)
28 // 参数4:isProduction 是否生产环境(开发状态:NO 生产状态:YES)
29 // 参数5:advertisingIdentifier 广告标识符IDFA(如不需要使用IDFA,传nil)
30 // 如需继续使用pushConfig.plist文件声明appKey等配置内容,请依旧使用[JPUSHService setupWithOption:launchOptions]方式初始化
31 [JPUSHService setupWithOption:launchOptions appKey:@"7ec12ec1b6f2ccde18fa39bf" channel:@"Publish channel" apsForProduction:NO advertisingIdentifier:nil];
32
33 return YES;
34 }
35
36 #pragma mark - 获取用户对应当前应用程序的deviceToken时就会调用
37 - (void)application:(UIApplication *)application
38 didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
39 {
40 // 获取deviceToken后,将deviceToken发送给JPush管理
41 [JPUSHService registerDeviceToken:deviceToken];
42 }
43
44 #pragma mark - iOS7以前,接收到服务器推送过来的消息就会调用
45 // 如果应用程序在后台,只有用户点击了推送消息,才会调用(但是iOS8以后苹果有了多任务,我们可以打开多任务开关)
46 // 如果应用程序在前台,会直接调用
47 // 注意:只有应用程序是打开状态(前台/后台),才会调用该方法
48 // 注意:如果应用程序是关闭状态(死亡),会调用didFinishLaunchingWithOptions,那么这个方法中,推送信息会放在launchOptions参数之中
49 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
50 {
51 [JPUSHService handleRemoteNotification:userInfo];
52 }
53
54 #pragma mark - iOS7以后,接收到服务器推送过来的消息就会调用
55 // iOS8以后用这个方法处理后台任务接收到远程通知
56 // 这个可以配合iOS8之后苹果推出的多任务
57 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
58 {
59 NSLog(@"userInfo = %@", userInfo);
60 #if 0
61 userInfo = {
62 "_j_msgid" = 3648087063;
63 aps = {
64 alert = "\U4f60\U6709\U65b0\U7684\U7248\U672c\U9700\U8981\U66f4\U65b0";
65 badge = 1;
66 "content-available" = 1;
67 sound = default;
68 };
69 }
70 #endif
71
72 // 设置角标(清空角标)
73 // 程序在后台或者在前台,直接不用设置角标
74 // 程序在关闭状态,才有角标更新,然后点击进入前台,角标清0
75 [application setApplicationIconBadgeNumber:0];
76
77 // UIBackgroundFetchResultNewData // 成功接收到数据
78 // UIBackgroundFetchResultNoData // 没有接收到数据
79 // UIBackgroundFetchResultFailed // 接收失败
80 [JPUSHService handleRemoteNotification:userInfo];
81 // 注意:在此方法中一定要调用这个block,告诉系统是否处理成功,以便于系统在后台中去更新UI等操作
82 completionHandler(UIBackgroundFetchResultNewData);
83 }
84
85 #pragma mark - 注册JPush通知失败
86 - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
87 {
88 NSLog(@"Error: %@", error);
89 }
90
91 @end
三、本地推送
1.概念
顾名思义,本地通知就是不需要联网就能够发出来的推送通知(不需要服务器的支持)
2.使用场景
常用来提醒用户完成一些任务;比如:清理垃圾、记账的App等等;一些不需要再联网使用的App,往往需要使用本地通知
3.代码实例
1 // ViewController.m文件
2 #import "ViewController.h"
3
4 @interface ViewController ()
5
6 @end
7
8 @implementation ViewController
9
10 - (void)viewDidLoad
11 {
12 [super viewDidLoad];
13
14 [self initUI]; // 界面
15 }
16
17 #pragma mark - 界面
18 - (void)initUI
19 {
20 // addNoteBtn
21 CGFloat addNoteBtnX = 20;
22 CGFloat addNoteBtnY = 100;
23 CGFloat addNoteBtnW = (self.view.bounds.size.width - addNoteBtnX * 2) / 2.0 - 10;
24 CGFloat addNoteBtnH = 35;
25 UIButton *addNoteBtn = [UIButton buttonWithType:UIButtonTypeSystem];
26 addNoteBtn.frame = CGRectMake(addNoteBtnX, addNoteBtnY, addNoteBtnW, addNoteBtnH);
27 [addNoteBtn setTitle:@"注册本地通知" forState:UIControlStateNormal];
28 addNoteBtn.titleLabel.font = [UIFont boldSystemFontOfSize:17];
29 addNoteBtn.tintColor = [UIColor whiteColor];
30 addNoteBtn.backgroundColor = [UIColor blackColor];
31 [addNoteBtn addTarget:self action:@selector(addNoteBtnClick:) forControlEvents:UIControlEventTouchUpInside];
32 [self.view addSubview:addNoteBtn];
33
34 // removeNoteBtn
35 CGFloat removeNoteBtnX = CGRectGetMaxX(addNoteBtn.frame) + 10;
36 CGFloat removeNoteBtnY = addNoteBtn.frame.origin.y;
37 CGFloat removeNoteBtnW = addNoteBtn.bounds.size.width;
38 CGFloat removeNoteBtnH = addNoteBtn.bounds.size.height;
39 UIButton *removeNoteBtn = [UIButton buttonWithType:UIButtonTypeSystem];
40 removeNoteBtn.frame = CGRectMake(removeNoteBtnX, removeNoteBtnY, removeNoteBtnW, removeNoteBtnH);
41 [removeNoteBtn setTitle:@"移除本地通知" forState:UIControlStateNormal];
42 removeNoteBtn.titleLabel.font = [UIFont boldSystemFontOfSize:17];
43 removeNoteBtn.tintColor = [UIColor whiteColor];
44 removeNoteBtn.backgroundColor = [UIColor blackColor];
45 [removeNoteBtn addTarget:self action:@selector(removeNoteBtnClick:) forControlEvents:UIControlEventTouchUpInside];
46 [self.view addSubview:removeNoteBtn];
47 }
48
49 #pragma mark - 点击事件
50 // 1.注册本地通知
51 - (void)addNoteBtnClick:(UIButton *)sender
52 {
53 // 1.创建本地通知对象
54 UILocalNotification *localNote = [[UILocalNotification alloc] init];
55
56 // 2.参数设置
57 // 指定通知发送时间(指定5秒后发送通知)
58 localNote.fireDate = [NSDate dateWithTimeIntervalSinceNow:5];
59 // 注意:在真实开发中一般情况下还是需要指定时区(让通知的时间跟随当前时区)
60 localNote.timeZone = [NSTimeZone defaultTimeZone];
61 // 指定通知内容
62 localNote.alertBody = @"这是通知的内容";
63 // 指定锁屏界面的信息
64 localNote.alertAction = @"这是锁屏界面的信息";
65 // 注册通知时可以指定将来点击通知之后需要传递的数据
66 localNote.userInfo = @{@"name" : @"Frank", @"age" : @"26", @"phone" : @"12345678912"};
67 // 设置应用程序的提醒图标
68 localNote.applicationIconBadgeNumber = 998;
69 // 设置通知重复的周期
70 localNote.repeatInterval = NSCalendarUnitSecond;
71 // 设置点击通知进入程序时候的启动图片
72 localNote.alertLaunchImage = @"Default";
73 // 收到通知播放的音乐
74 localNote.soundName = @"buyao.wav";
75
76 // 3.注册通知
77 UIApplication *application = [UIApplication sharedApplication];
78 [application scheduleLocalNotification:localNote];
79 }
80 // 2.移除本地通知
81 - (void)removeNoteBtnClick:(UIButton *)sender
82 {
83 UIApplication *application = [UIApplication sharedApplication];
84 // 清空所有本地通知
85 [application cancelAllLocalNotifications];
86 // 清空指定本地通知
87 // [application cancelLocalNotification:nil];
88 }
89
90 #pragma mark - 本地通知(UILocalNotification)知识点
91 // 1.指定通知发送的时间
92 // @property(nullable, nonatomic,copy) NSDate *fireDate;
93
94 // 2.指定通知发送的时区
95 // @property(nullable, nonatomic,copy) NSTimeZone *timeZone;
96
97 // 3.重复的周期(接收的是枚举值)
98 // @property(nonatomic) NSCalendarUnit repeatInterval;
99
100 // 4.重复周期(接收的是日历对象)
101 // @property(nullable, nonatomic,copy) NSCalendar *repeatCalendar;
102
103 // 5.通知的内容
104 // @property(nullable, nonatomic,copy) NSString *alertBody;
105
106 // 6.是否需要滑动事件(默认为YES)
107 // @property(nonatomic) BOOL hasAction;
108
109 // 7.锁屏状态的标题
110 // @property(nullable, nonatomic,copy) NSString *alertAction;
111
112 // 8.点击通知后的启动图片名
113 // @property(nullable, nonatomic,copy) NSString *alertLaunchImage;
114
115 // 9.收到通知播放的音乐
116 // @property(nullable, nonatomic,copy) NSString *soundName;
117
118 // 10.图标提醒数字
119 // @property(nonatomic) NSInteger applicationIconBadgeNumber;
120
121 // 11.额外的信息
122 // @property(nullable, nonatomic,copy) NSDictionary *userInfo;
123
124 @end