前言
之前就写过ios和安卓的本地通知栏的消息通知:《安卓和ios显示消息通知【一】》和《安卓和ios显示消息通知【二】》,今天又想起来这个东西,所以就有折腾了下IOS的本地通知和远程通知,经过试验,达到了消息推送的目的,记录下实现过程和注意事项。
一、本地通知栏通知
1.1、本地通知的创建和使用
本地通知其实在之前的文章和网上就已经很详细的说了,主要是UILocalNotification的使用,本地通知主要应用在固定时间的通知事件,比如日历、活动提醒等。
主要就是各个参数的使用
NSLog(@"添加本地通知");
//ios8.0以上的系统需要注册通知
if (D_ISHight(8.0)) {
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeSound|UIUserNotificationTypeBadge categories:nil]]; //注册通知
}
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
//设置固定时间通知
if (sender.tag == LOCALNOTIFICATION1) {
NSDateFormatter * forma = [[NSDateFormatter alloc] init];
[forma setDateFormat:@"HH:mm"];
localNotification.fireDate = [forma dateFromString:@"12:00"]; //12点提醒
[localNotification setRepeatInterval:NSCalendarUnitDay]; //每天12点
}
//设置重复间隔时间
else if (sender.tag == LOCALNOTIFICATION1){
NSDate *date =[[NSDate alloc] init];
localNotification.fireDate = [date dateByAddingTimeInterval:10];//重复间隔的时长
[localNotification setRepeatInterval:NSCalendarUnitSecond];//时间间隔单位,秒
}
[localNotification setTimeZone:[NSTimeZone defaultTimeZone]]; //时区
//ios8.2以上的系统可以设置标题
if (D_ISHight(8.2)) {
[localNotification setAlertTitle:@"提醒标题"];
}
[localNotification setAlertBody:@"提醒内容:Damon"];
[localNotification setAlertAction:@"锁屏时显示的动作标题"]; //在锁屏时显示的动作标题(完整测标题:"滑动来" + alertAction)
[localNotification setApplicationIconBadgeNumber:1]; //设置提醒的软件右上角的小红点
[localNotification setSoundName:UILocalNotificationDefaultSoundName];//默认声音
//或者指定文件名localNotification.soundName = @"123.wav";
[localNotification setAlertLaunchImage:@"demo.png"];
[localNotification setUserInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Damon",@"name",@"blog",@"type", nil]];//设置属性
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification]; //调用通知
在本地消息调用之后,就会在通知栏显示了,点击通知栏的消息之后,会自动打开这个软件,需要注意的是软件不同的状态,点击通知栏调用的函数也不同。
1、软件如果是在后台运行,并没有退出,调用的是AppDelegate的这个函数
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
2、软件已经主动退出,或者后台超过时间退出,点击通知栏消息之后就只会调用这个函数
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
3、所以不同的状态,调用的函数也不同,那么想获取是哪个通知调用的函数方法也不相同了,如果是后台运行状态下,想知道某个通知,可以通过不同的userInfo这样实现:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSLog(@"Application did receive local notifications");
// 在这里写跳转代码
// 如果是应用程序在前台,依然会收到通知,但是收到通知之后不应该跳转
if (application.applicationState == UIApplicationStateActive)
{
return;
}
if (application.applicationState == UIApplicationStateInactive) {
// 当应用在后台收到本地通知时执行的跳转代码
//可以通过设置通知时的userinfo过滤某条通知
if ([notification.userInfo[@"name"] isEqualToString:@"Damon"]) {
NSLog(@"damon");
}
//可以得到所有的通知
for (UILocalNotification *noti in [[UIApplication sharedApplication] scheduledLocalNotifications]) {
NSLog(@"%@",noti.fireDate);
}
//通知之后可以取消对应的通知
[[UIApplication sharedApplication] cancelLocalNotification:notification];
}
[self jump:1];
}
4、而如果app已经退出,那么在启动函数里面获取通知就需要通过launchOptions里面的UIApplicationLaunchOptionsLocalNotificationKey来实现,比如这样:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//当应用退出之后,点击通知跳转到应用会走这个函数,而不是didReceiveLocalNotification
UILocalNotification * local = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if ([local.userInfo[@"name"] isEqualToString:@"Damon"]) {
NSLog(@"damon");
[self jump:2];
}
return YES;
}
这里给出一个Demo,仅仅包含本地通知,这样看起来不会太乱,远程通知继续往下看
1.2、本地通知栏通知demo下载:
GitHub下载:https://github.com/DamonHu/HudongBlogDemo/tree/master/IOSNotifiction
二、远程通知
2.1、远程推送原理
ios上面,苹果允许的通知就是远程通知,今天查了下几个网站,发现QQ,微信也是这样的,以QQ为例,当QQ在后台运行的时候,有人给你发消息的时候,腾讯服务器向苹果的远程推送服务器(APNS)发送通知,苹果推送服务器再把这个消息推送到你的手机,如果一直后台运行,那么就是这样推送。当你打开QQ,开始在QQ界面内开始聊天等等,这时候推送就直接是腾讯软件之间的推送了。网上找到了下面这个图说的很形象。
[](http://ddceo.com/blog/content/uploadfile/201608/4a471472048116.png)
远程推送的过程就是这样的
1、应用启用推送通知功能,需要用户确认,向苹果请求一个device token;
2、应用收到设备识别ID(device token),相当于接收推送通知的地址,苹果的APNS知道你这个手机的标识;
3、应用将设备识别ID(device token)发送到你开发的服务器,服务器记录;
4、当有推送通知的需要时,你就可以通过你开发的服务组件把想要发送的消息和(device token)发送信息到苹果的服务器上;
5、苹果通过(device token)在通知服务上将信息推送到用户的设备上。
这个deviceToken是请求远程通知的时候,苹果返回给应用的一个唯一标识,但是并不是手机的uuid,所以用uuid是不行的。
苹果的远程推送是长连接,这样数据传输速度快,并且一直保持最新状态,当然不排除苹果服务器抽风的情况,这样就会导致服务器发送的通知没有通知到用户,但是这锅一般都是软件自己的服务器背了。
2.2、创建远程通知的准备
远程通知不像本地通知那样只消耗本地资源,远程通知还要使用苹果的服务器推送,所以需要真机和已经注册的开发者帐号,现在是测试,所以创建的都是develop的开发证书,不是发布证书,如果是要上架使用,那么就使用对应的发布证书。
2.2.1、创建证书和配置文件
去苹果开发者后台:https://developer.apple.com,创建一个Apple Push Notification service SSL (Sandbox)证书,创建这个证书和其他证书的流程一样。
需要注意的是在创建这个证书里面,选择的appid都是指定的全称,不是带有*这种泛用的,这appid也是在后台添加的
创建完成之后,就像这个样子的,这就是我创建的推送证书和自己的开发证书,推送证书的appleid就是全称的com.caribbean.test1:
创建完证书之后,可以在appid的详情中看到那个appid的推送服务是可用的
证书创建完毕之后,就继续创建配置文件,配置文件选择你开发的那个证书就可以了,我就是选择我的开发证书创建的配置文件,配置文件的appid必须也是这个com.caribbean.test1,项目的bundle id也是com.caribbean.test1,三个要一致,如果配置文件不是这个,而是用的*这种通用的,我测试了下是收不到消息的。
之后配置项目的证书,配置文件用来真机测试即可
2.2.2、远程通知前端开发
远程通知过程就是 注册通知-》苹果服务器返回token -》本地服务器保存token -》本地服务器发送token通知到苹果服务器 -》苹果服务器下发通知
1、注册通知:
NSLog(@"添加远程通知");
//远程通知授权
if (D_ISHight(8.0)) {
// 1.注册UserNotification,以获取推送通知的权限
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
// 2.注册远程推送
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeNewsstandContentAvailability | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound];
}
2、返回token
苹果服务器是在appdelegate的回调中返回token的,这里写一个提取token的函数
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSLog(@"didRegisterForRemoteNotificationsWithDeviceToken");
// NSLog(@"%@",deviceToken);
NSLog(@"%@",[[[[deviceToken description] stringByReplacingOccurrencesOfString: @"<" withString: @""] stringByReplacingOccurrencesOfString: @">" withString: @""] stringByReplacingOccurrencesOfString: @" " withString: @""]);
}
如果不使用这个提取函数,那么他的token是一个<32e7cf5f 8af9a8d4 2a3aaa76 7f3e9f8e 1f7ea8ff 39f50a2a e383528d 7ee9a4ea>这种样子的。
然后就是把这个返回的token发送给服务器保存即可。当然还有一个接受到消息的回调函数,回调函数的使用和本地消息通知的回调函数的使用是一样的。
//远程通知
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSLog(@"sssss%@",userInfo);
}
这时候前端工作已完成,后面的发送通知什么的都是后端的事情了。
如果仅仅是前端开发,这已经结束了,如果你想自己php测试,那就继续往后看。
2.2.3、后端php测试发送消息
因为开发了,肯定要测试,所以这里就找到了一个php测试的例子。
准备东西:
在桌面创建一个文件夹,把你安装的那个通知证书在钥匙串工具中导出来一份放到这个文件夹,格式是p12的
然后之前下载的那个通知证书也放到这个文件夹,是cer格式的。
然后开始制作php执行的环境。
1、打开终端,cd到文件夹里面,执行下面这个命令,把.cer的SSL证书转换为.pem文件,注意文件名字替换成你的
openssl x509 -in aps_development.cer -inform der -out PushChatCert.pem
2、把私钥Push.p12文件转化为.pem文件,这个需要输入密码,你自己定义一个密码即可,至少四位的,后面会到那个PHP文件替换:
openssl pkcs12 -nocerts -out PushChatKey.pem -in 证书.p12
3、对生成的这两个pem文件再生成一个pem文件,来把证书和私钥整合到一个文件里:
cat PushChatCert.pem PushChatKey.pem > ck.pem
这样,我们的文件就制作完了。下面进入测试阶段
为了测试证书是否工作,执行下面的命令:
telnet gateway.sandbox.push.apple.com 2195
它将尝试发送一个规则的,不加密的连接到APNS服务。如果你看到上面的反馈,那说明你的MAC能够到达APNS。按下Ctrl+C关闭连接。如果得到一个错误信息,那么你需要确保你的防火墙允许2195端口。一般这里都不会出现什么问题。
下面我们要使用我们生成的SSL证书和私钥来设置一个安全的链接去链接苹果服务器:
openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert PushChatCert.pem -key PushChatKey.pem
执行完这一句命令后需要我们输入密语
Enter pass phrase for PushChatKey.pem:
我们输入密码按回车
你会看到一个完整的输出,让你明白OpenSSL在后台做什么。如果链接是成功的,你可以随便输入一个字符,按下回车,服务器就会断开链接,如果建立连接时有问题,OpenSSL会给你返回一个错误信息。
这样php已经配置成功了。
2.2.4、php文件配置
在下面的demo工程里面,下载pushMe.php文件,把php文件中的$passphrase = '123456';
修改为你自己的密码,$deviceToken
修改为你的真机返回的token,然后把这个pushMe.php也复制到这个文件夹,执行命令即可收到该消息通知
php pushMe.php
你可以从demo下载,也可以直接创建一个php文件保存该代码
<?php
// Put your device token here (without spaces):
$deviceToken = 'c91c752bfa62b8079163d5657042a6147dbc8c2f9dadb03f36eea82601df68d8';
// Put your private key's passphrase here:密语
$passphrase = '123456';
// Put your alert message here:
$message = '这是一条推送消息';
////////////////////////////////////////////////////////////////////////////////
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Open a connection to the APNS server
$fp = stream_socket_client(
'ssl://gateway.sandbox.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp)
exit("Failed to connect: $err $errstr" . PHP_EOL);
echo 'Connected to APNS' . PHP_EOL;
// Create the payload body
$body['aps'] = array(
'alert' => $message,
'sound' => 'default'
);
// Encode the payload as JSON
$payload = json_encode($body);
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
if (!$result)
echo 'Message not delivered' . PHP_EOL;
else
echo 'Message successfully delivered' . PHP_EOL;
// Close the connection to the server
fclose($fp);
?>
2.2.5、demo下载地址和pushMe.php文件
GitHub下载地址:https://github.com/DamonHu/HudongBlogDemo/tree/master/IOSRemoteNotifiction
2.2.6、效果
三、参考文章
版权属于:东哥笔记 - DongGe.me
本文链接:https://dongge.org/blog/348.html
自2017年12月26日起,『转载以及大段采集进行后续编辑』须注明本文标题和链接!否则禁止所有转载和采集行为!