一.推送的原理和流程(着急做推送的可以跳过这一步)
首先给大家推荐一个介绍推送机制很优秀的帖子:http://www.jianshu.com/p/ac397a033dec ,里面关于本地推送和远程推送的介绍都很详细,至少我看了感觉还是收获很多的.尤其是里面有几张图片不知道是博主在哪里找的,但是真的是一看就透,太赞了,所以我果断盗过来了0.0. 这里我对推送的流程做了一个简单的叙述,力求用最简单的语言能说明整个推送的机制.
当我们的苹果手机联网的时候,会自动与苹果的服务器建立长连接,长连接的好处有很多,比如系统升级、时间校准、数据传输和响应比较快以及数据可以保持最新状态等功能.上面这两张图片简单的讲述了推送的流程:
1.首先我们需要将自己设备的UDID和应用的Bundle Identifier发送到苹果的服务器,然后苹果的服务器会返回给我们一个DeviceToken,这个在我看来就是创建推送证书和描述文件的过程.
2.我们将包含手机和应用标示的打包文件上传到做推送的服务器上去,当我们从推送服务器的后台发起推送消息的时候,推送服务器会将我们的DeviceToken和需要发送的消息Message发送到苹果的APNS(Apple push Notification Service)服务器.
3.当苹果的服务器收到DeviceToken和需要发送的消息Message时,会根据DeviceToken中的UDID查找设备,根据DeviceToken中的Bundle Identfier查找该应用,并将Message发送到该设备上.
下面是以QQ服务器为栗子说明的即时通讯的机制:
3.png二.具体流程
我们创建一个名为TestDemo的工程,我是使用Xcode8.1来开发的,工程名为PushDemo,创建好的工程界面如下(Xcode8)
3.png从Xcode8之后,Xcode提供了自动管理证书的功能,这个用起来很方便,我目前在工程中用到的最多的地方就是创建好一个Demo之后,如果想真机运行的话,那么只需要在Team选择框里选择我的开发账户,接下来下面会出现一个加载提示圈,等它加载完了就可以在真机上运行了,这个过程实际上是Xcode使用你当前的BundleId去该账户的开发这中心创建了对应的AppId和描述文件,但是我们既然是作为一个开发流程的记录,就自己来创建这些东西,所以,我们取消选择Automatically manage signing选项.此时界面如下:
4.png- 首先我们先去官网创建AppID和描述文件.
我们是要集成推送的,所以我们需要用到cer文件,这个东西实际上就是苹果给开发者颁发的一个证书,我们需要将它导入到我们的AppId配置里,否则的话是无法集成推送的,还记得安装应该的时候会提示"无法安装为认证发布者的应用"之类的信息么,我猜测这个cer文件就是我们身份的标示,使我们开发的应用可以供人们正常安装使用,关于证书有一篇很详细的帖子,希望了解证书之类信息的看官可以去瞅瞅:http://m.blog.csdn.net/article/details?id=8617788
创建cer文件的流程很简单,打开"钥匙串访问"(虽然很好找,但是还是把图贴出来吧,怕小朋友迷路)
![Uploading 6_078927.png . . .]
5.png
打开钥匙串之后点击"从证书颁发机构请求证书"
7.png邮箱和常用名随便填写,记住下面的选择框选择"存储到磁盘"
8.png点击存储
9.png已经在桌面保存了
10.png到此,我们已经创建好了cer文件,接着我们去开发者中心创建AppId和描述文件
- 创建AppId和描述文件
首先进入开发者中心,百度搜索Apple Developer,(哎 真的是详细到家了啊,我都人不下去了)
上图
输入开发者账户,登录进去
12.png你将看到这个页面
13.png点击看到:
14.png输入AppId文件名和BundleId
15.png选中下面的PushNotifications
16.png点击Continue:
17.png点击register:
18.png点击Done回到AppId列表页面
19.png在AppId列表页面可以看到我们的AppID了
20.png但是,还没有完成,因为我们是要做推送的,所以需要上传我们的cer文件
,点击我们的AppId,在展开的详情里可以看到:
Push Notification的两个指示灯还是黄色的状态,我们要将它启用,点击Edit,在点开的页面里滑动至底部,记得要选中Push Notification按钮,接着点击上方的开发证书下的创建证书按钮:
22.png点击Continue
23.png点击 choose file:
24.png将我们从开发机构请求的证书传上去,之后点击Register:
25.png点击Register之后的页面,点击download,将其下载到桌面上,download之后记得点击done完成文件创建:
26.png桌面上的文件:
27.png现在我们就完成了给AppID创建开发者证书,然后我们要给它创建发布者证书,点击Done之后回到AppIds列表,如果找不到的话,点击右边的App IDs
28.png点开项目的AppId,此时界面如下,点击最下面的CreateCertificate,开始给AppID创建发布者证书,给AppId创建发布者证书流程跟创建开发者证书是一样的!给AppId创建发布者证书流程跟创建开发者证书是一样的!给AppId创建发布者证书流程跟创建开发者证书是一样的!重要的事情说三遍!!因为我不贴出来创建发布证书的图了,所以各位根据创建开发证书的流程再走一遍就好,同样也要将发布者证书下载到本地.:
29.png 30.png当创建好之后在回到这个页面时,应该显示如下所示:
31.png此时本地我们下载的文件如下:
32.png然后将这两个证书拖到钥匙串里,步骤如下:
首先打开钥匙串:
然后先点击:系统-证书,然后将两个文件拖进去,会提示你输入开机密码,输入就好了(建议添加之前先对这个界面截屏,添加完之后可以对比刚刚添加了那些文件)
35.png添加完之后是这个样子,画框的是我们的证书
36.png然后选择左边的"登录"选项,可以看到我们刚才创建的证书
37.png选中第一个证书,然后右键(你懂得右键的意思),选择导出...
38.png选择导出为P12文件,存储在桌面上,获取到P12文件.对这两个证书进行同样的操作.(记得标题有(Develop)的起名为Product文件,第二个证书导出的时候起名为Develop,名字可以自己定,只是为了区别)
39.png然后会提示你输入密码,这里我设置的密码是123456,自己设定好一定要记住,一会儿要用.
40.png然后可以在桌面上看到我们导出的P12文件啦
41.png现在我们就完成了所有的证书的创建,可以去环信上创建我们的应用啦.
3.创建真机调试文件以及导入到项目中
因为必须要进行真机测试,而且我们关闭了自动管理证书,就导致Xcode8不会自动帮我们生成证书,所以我们要自己创建真机调试证书并导入到项目中去,流程如下:
创建描述文件:
42.png选择开发模式,下一步:
43.png选择对应的AppID,选择我们刚才创建的AppId:
44.png选择开发团队,我一般都是全选的,下一步:
45.png选择真机调试的机器,全选,下一步:
46.png下一步:
47.png将创建好的描述文件下载下来,放到桌面上:
48.png创建好的描述文件:
49.png首先选择debug模式下载的真机调试描述文件:
50.png选择桌面上刚刚下载的描述文件:
51.png使用同样的步骤,选择Release模式下的真机调试文件,一模一样的操作,不贴图了.两个文件都导入进去之后,插上真机,就可以进行真机调试了.
4.在环信创建我们的应用
首先百度搜索环信,打开他们的官网,先注册账户,注册过的可以跳过了,上图:
注册的时候选择"注册即时通讯云"
注册的时候需要填写各种信息,按照格式填写就好了,填写完之后登陆,点击创建应用
53.png填写应用信息
54.png填写完如下图咯
55.png然后需要上传我们的P12文件,图片很清晰- -,不多说,第一次我选择上传的是生产证书:
56.png第二次上传开发证书:
57.png至此,我们的证书开发也都上传完了,路漫漫其修远兮,开始集成环信到代码里吧
5.集成环信到项目中
首先在这里下载最新的SDK(截至到写本文时最新的SDK为)
http://www.easemob.com/download/im 环信推送SDK下载链接
点击iOS的最新SDK下载,这里下载的是V3.x的SDK
下载到桌面是这个样子
59.png我们只需要将画圈的两个文件夹导进去工程里就好了,其他的用不上
60.png导进去之后文件列表是这样,编译会出错别急,慢慢改.
61.png向项目里添加需要的库
62.png上面的图片是截取的环信官方文档,我添加完是这个样子的:
63.png方便复制库名的文字:
CoreMedia.framework
AudioToolbox.framework
AVFoundation.framework
MobileCoreServices.framework
ImageIO.framework
libc++.dylib
libz.dylib
libstdc++.6.0.9.dylib
libsqlite3.dylib
(如果使用的是 xcode7,后缀为 tbd。)
这一步很重要,因为SDK 不支持 bitcode,所以要将 Build Settings → Linking → Enable Bitcode 中设置 NO。
command+B编译工程,大量爆红.别着急,修改我们的PCH文件就好了
在PCH文件添加
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#endif
将我们所有定义和添加的头文件和宏定义,都放在#ifdef OBJC和#endif中间
就可以解决这个问题.
然后在项目里打开推送:
65.png6.测试是否集成成功
首先,我们去环信的后台给我们的应用添加一个用户
66.png用户名我设置成了:13088888888 密码设置成了:222222
67.png接着我们要去appledate.m文件里添加东西了,很重要一步,废话不多说,直接贴出来需要配置的代码,直接拿去用0.0,需要添加的东西我在注释里注释的很明白...
记得要导进去头文件
import "EMSDK.h"
@interface AppDelegate ()<EMChatManagerDelegate>
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//AppKey:注册的AppKey,点击"应用概述"可以看到AppKey,粘贴过来就可以。
//apnsCertName:推送证书名,填写你的开发证书或者发布证书名,就是上传到环信后台的两个中的一个,什么环境下测试使用什么环境的证书。
EMOptions *options = [EMOptions optionsWithAppkey:@"1192161108178165#testpushdemo"];
options.apnsCertName = @"Develop";
[[EMClient sharedClient] initializeSDKWithOptions:options];
//登录环信 这里使用的是我刚才在环信后台创建的账户名和密码,使用这个账户登录,到时候如果在后台给客户端发消息的话,就可以找到该用户
[[EMClient sharedClient] loginWithUsername:@"13051698888"
password:@"222222"
completion:^(NSString *aUsername, EMError *aError) {
if (!aError) {
NSLog(@"环信登陆成功");
EMPushOptions *emoptions = [[EMClient sharedClient] pushOptions];
//设置有消息过来时的显示方式:1.显示收到一条消息 2.显示具体消息内容.
//自己可以测试下
emoptions.displayStyle = EMPushDisplayStyleSimpleBanner;
[[EMClient sharedClient] updatePushOptionsToServer];
} else {
NSLog(@"环信登陆失败");
}
}];
/**
注册APNS离线推送 iOS8 注册APNS
*/
if ([application respondsToSelector:@selector(registerForRemoteNotifications)]) {
[application registerForRemoteNotifications];
UIUserNotificationType notificationTypes = UIUserNotificationTypeBadge |
UIUserNotificationTypeSound |
UIUserNotificationTypeAlert;
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:notificationTypes categories:nil];
[application registerUserNotificationSettings:settings];
}
else{
UIRemoteNotificationType notificationTypes = UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeSound |
UIRemoteNotificationTypeAlert;
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:notificationTypes];
}
//添加监听在线推送消息
[[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];
return YES;
}
//监听环信在线推送消息
- (void)messagesDidReceive:(NSArray *)aMessages{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"收到环信通知" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
[alertView show];
//aMessages是一个对象,包含了发过来的所有信息,怎么提取想要的信息我会在后面贴出来.
}
// 将得到的deviceToken传给SDK
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
[[EMClient sharedClient] bindDeviceToken:deviceToken];
}
// 注册deviceToken失败
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
NSLog(@"error -- %@",error);
}
// APP进入后台
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[EMClient sharedClient] applicationDidEnterBackground:application];
}
// APP将要从后台返回
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[EMClient sharedClient] applicationWillEnterForeground:application];
}
上面的几个方法在appdelegate里是必须重写的,不然会直接导致推送不成功.其中.需要重点说明的是:
只有在应用完全退出被杀掉的状态下,才可以收到环信推送的通知;
如果要发送在线的通知,需要在messagesDidReceive方法里获取到环信推送的消息之后给用户发起一个本地通知,这个大家可以自己研究下.
通过设置emoptions.displayStyle = EMPushDisplayStyleSimpleBanner;(上面代码有)可以设置有通知过来的时候的显示方式,显示一个提示或者显示完整的消息.
上传证书下面填写的应用包名,指的是你的BundleID !!!!我在这里踩了坑,切记!!.
测试推送:
在应用完全退出的情况下(使用在环信注册的账户登录一次,确认登录成功之后再完全退出),选中我们的用户,点击发送消息:
68.png点击发送:
69.png测试结果:
70.png2.程序在线的时候测试推送,还是发送"你好啊",然后我们在messagesDidReceive拦截环信的EMMessage对象,针对EMMessage对象的解析方式如下,完整的抽取环信推送消息的方法:
- (void)messagesDidReceive:(NSArray *)aMessages{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"收到环信通知" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
[alertView show];
for (EMMessage *message in aMessages) {
EMMessageBody *msgBody = message.body;
switch (msgBody.type) {
case EMMessageBodyTypeText:
{
// 收到的文字消息
EMTextMessageBody *textBody = (EMTextMessageBody *)msgBody;
NSString *txt = textBody.text;
NSLog(@"收到的文字是 txt -- %@",txt);
}
break;
case EMMessageBodyTypeImage:
{
// 得到一个图片消息body
EMImageMessageBody *body = ((EMImageMessageBody *)msgBody);
NSLog(@"大图remote路径 -- %@" ,body.remotePath);
NSLog(@"大图local路径 -- %@" ,body.localPath); // // 需要使用sdk提供的下载方法后才会存在
NSLog(@"大图的secret -- %@" ,body.secretKey);
NSLog(@"大图的W -- %f ,大图的H -- %f",body.size.width,body.size.height);
NSLog(@"大图的下载状态 -- %u",body.downloadStatus);
// 缩略图sdk会自动下载
NSLog(@"小图remote路径 -- %@" ,body.thumbnailRemotePath);
NSLog(@"小图local路径 -- %@" ,body.thumbnailLocalPath);
NSLog(@"小图的secret -- %@" ,body.thumbnailSecretKey);
NSLog(@"小图的W -- %f ,大图的H -- %f",body.thumbnailSize.width,body.thumbnailSize.height);
NSLog(@"小图的下载状态 -- %u",body.thumbnailDownloadStatus);
}
break;
case EMMessageBodyTypeLocation:
{
EMLocationMessageBody *body = (EMLocationMessageBody *)msgBody;
NSLog(@"纬度-- %f",body.latitude);
NSLog(@"经度-- %f",body.longitude);
NSLog(@"地址-- %@",body.address);
}
break;
case EMMessageBodyTypeVoice:
{
// 音频sdk会自动下载
EMVoiceMessageBody *body = (EMVoiceMessageBody *)msgBody;
NSLog(@"音频remote路径 -- %@" ,body.remotePath);
NSLog(@"音频local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在(音频会自动调用)
NSLog(@"音频的secret -- %@" ,body.secretKey);
NSLog(@"音频文件大小 -- %lld" ,body.fileLength);
NSLog(@"音频文件的下载状态 -- %u" ,body.downloadStatus);
NSLog(@"音频的时间长度 -- %u" ,body.duration);
}
break;
case EMMessageBodyTypeVideo:
{
EMVideoMessageBody *body = (EMVideoMessageBody *)msgBody;
NSLog(@"视频remote路径 -- %@" ,body.remotePath);
NSLog(@"视频local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在
NSLog(@"视频的secret -- %@" ,body.secretKey);
NSLog(@"视频文件大小 -- %lld" ,body.fileLength);
NSLog(@"视频文件的下载状态 -- %u" ,body.downloadStatus);
NSLog(@"视频的时间长度 -- %u" ,body.duration);
NSLog(@"视频的W -- %f ,视频的H -- %f", body.thumbnailSize.width, body.thumbnailSize.height);
// 缩略图sdk会自动下载
NSLog(@"缩略图的remote路径 -- %@" ,body.thumbnailRemotePath);
NSLog(@"缩略图的local路径 -- %@" ,body.thumbnailLocalPath);
NSLog(@"缩略图的secret -- %@" ,body.thumbnailSecretKey);
NSLog(@"缩略图的下载状态 -- %u" ,body.thumbnailDownloadStatus);
}
break;
case EMMessageBodyTypeFile:
{
EMFileMessageBody *body = (EMFileMessageBody *)msgBody;
NSLog(@"文件remote路径 -- %@" ,body.remotePath);
NSLog(@"文件local路径 -- %@" ,body.localPath); // 需要使用sdk提供的下载方法后才会存在
NSLog(@"文件的secret -- %@" ,body.secretKey);
NSLog(@"文件文件大小 -- %lld" ,body.fileLength);
NSLog(@"文件文件的下载状态 -- %u" ,body.downloadStatus);
}
break;
default:
break;
}
}
}
发送成功之后打印结果如下:
2016-12-01 16:03:26.060088 PushDemo[1392:450230] 收到的文字是 txt -- 你好啊
三.结语
至此,我们就成功集成了环信推送到我们的项目中.另外提供一些在做推送的时候经常会用到的小方法
设置应用图标右上角数字角标.
UIApplication *application = [UIApplication sharedApplication]; [application setApplicationIconBadgeNumber:3];
如果推送证书那里没看特别明白的话,提供一个创建推送证书的链接:http://www.jianshu.com/p/79061dda87e3
设置推送过来时候的apns昵称:
[[EMClient sharedClient] setApnsNickname:@"推送昵称"];
网友评论