一、前言

推送分为远程推送和本地推送。

 

二、远程推送

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教程一步步来

  这里只是提供制作证书的步骤:

ios 消息推送 频次 ios消息推送机制优缺点_ios 消息推送 频次

 

ios 消息推送 频次 ios消息推送机制优缺点_推送_02

ios 消息推送 频次 ios消息推送机制优缺点_服务器_03

ios 消息推送 频次 ios消息推送机制优缺点_ios 消息推送 频次_04

ios 消息推送 频次 ios消息推送机制优缺点_推送_05

ios 消息推送 频次 ios消息推送机制优缺点_服务器_06

ios 消息推送 频次 ios消息推送机制优缺点_服务器_07

ios 消息推送 频次 ios消息推送机制优缺点_推送_08

ios 消息推送 频次 ios消息推送机制优缺点_ios 消息推送 频次_09

ios 消息推送 频次 ios消息推送机制优缺点_服务器_10

ios 消息推送 频次 ios消息推送机制优缺点_App_11

ios 消息推送 频次 ios消息推送机制优缺点_ios 消息推送 频次_12

ios 消息推送 频次 ios消息推送机制优缺点_服务器_13

ios 消息推送 频次 ios消息推送机制优缺点_推送_14

ios 消息推送 频次 ios消息推送机制优缺点_App_15

ios 消息推送 频次 ios消息推送机制优缺点_App_16

ios 消息推送 频次 ios消息推送机制优缺点_App_17

ios 消息推送 频次 ios消息推送机制优缺点_服务器_18

ios 消息推送 频次 ios消息推送机制优缺点_服务器_19

ios 消息推送 频次 ios消息推送机制优缺点_推送_20

ios 消息推送 频次 ios消息推送机制优缺点_ios 消息推送 频次_21

ios 消息推送 频次 ios消息推送机制优缺点_服务器_22

  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