苹果消息推送服务教程:第一部分(共2部分)
这是iOS教程团队的Matthijs Hollemans编写的一篇教程, Matthijs Hollemans是一个经验丰富的iOS开发者和设计师。 在iOS系统中,在后台运行的程序能够进行的操作是非常有限的。这种限制是为了节省手机电池。 但是,如果你需要在用户没有使用你的程序的情况下给
这是iOS教程团队的Matthijs Hollemans编写的一篇教程, Matthijs Hollemans是一个经验丰富的iOS开发者和设计师。
在iOS系统中,在后台运行的程序能够进行的操作是非常有限的。这种限制是为了节省手机电池。
但是,如果你需要在用户没有使用你的程序的情况下给他们推送消息该怎么办呢?
打个比方,用户收到了一个来自推特的信息,他喜欢的球队在比赛中取胜,或者他的晚饭准备好了。因为现在用户并没有在使用我们的程序,程序本身无法听取这些事件。
幸运的是,苹果系统对此已有了解决办法。你并不需要让程序不停地听取事件或者在后台跑运算。你只需要编写一个服务器组件来完成这个任务。
在一个特定的事件发生时,那个服务器的组件就可以给我们的程序发一个推送信息!推送信息可以做如下三件事:
- 显示一条信息
- 播放一小段提示音乐
- 在程序的小图标边上放置一个数量标志
你可以随意组合这些选项;比如你可以播放提示音并放置数量标志,而不显示任何信息。
在这个有两部分的教程中,你会用苹果推送服务器(APNS)来完成一个有消息推送功能的简单的程序。
在第一部分,你会学习如何接收推送的信息。
这篇教程针对的是中级或者高级的iOS开发者。如果你对iOS还处于入门阶段,你应该先看看这个网站上一些初级教程. 并且,我也建议你可以先看看下面这两篇教程(或者有类似的经验):
- 如何为iOS程序写简单的PHP/MySQL网络服务
- 如何在iOS程序中使用网络服务
那就让我们开始吧!
文章概略
为你的app加入信息推送是需要付出很多努力的。这个任务有很多个部分。下面是一个概要:
- 程序启用消息推送功能。用户必须确认他希望接受这些推送信息。
- 那个程序接收一个“设备标记码”。你可以把这个设备标记码理解为推送信息的地址。
- 那个程序将这个设备标记码发送到服务器。
- 每当任何关于你的程序的事件发生时,那个服务器会将信息发送到苹果的推送信息服务(APNS)。
- APNS 将这个信息再推送到用户的设备上
用户的设备收到这个信息时,会有提示窗口,播放提示音或者更新app的数量标志。用户可以在提示窗口中直接开启这个app。我们的app从这里接过推送信息的内容并能自定义处理这个信息的逻辑。
有人会问,iOS4中已经有了本地提示以及多重任务执行,那推送信息提示还有用吗?答案:“当然啦”!
本地提示仅能用于定时的事件。无限制的后台运算也仅限用于网络通话,导航和背景音乐类的app。如果在程序已经进入后台运行还想给用户信息提示,那我们仍然需要使用推送信息提示。
在这个教程中,我会详细解释推送信息提示是怎么实现的,以及如何在你的app中使用它。需要学的东西很多,让我们现在开始吧!
推送信息提示准备工作
在你的app中加入推送信息提示,你需要:
一台 iPhone 或者 iPad。 你需要真实的设备因为推送信息提示在模拟器中不能用。
你必须是注册的iOS开发者。 每个要加入推送信息的app的需要一个新的App ID,provisioning profile 和SSL证书。你可以在iOS Provisioning Portal来完成这些所需操作。
如果你想跟着这个教程中的例子,那你就需要创建新的provisioning profile和SSL 证书;你不能用我的。因为获取正确的证书很重要,我会一步步解释如何获取这个证书。
一个连接到网上的服务器。 推送信息是由这个服务器发送出来的。开发期间你可以用你的苹果电脑作为服务器(我们在这个教程中就会这样做),但是在app上线后,你至少需要一个VPS(虚拟私人服务器)。
一个简单的共享的服务器账号是不够的。你需要能够在服务器上跑后台线程,安装SSL证书以及对外在特定端口建立TLS链接。
大多数共享服务器的提供者是不会让你这么做的。所以,我强烈建议你用 Linode类型的服务器.
解析消息推送服务/h2>
你的服务器负责创建被推送的消息,所以我们应该来了解一下这个服务器是怎么做到的。
一个推送消息会包含设备标记码,信息负载和一些其他的字节。那个信息负载就是我们要发到设备上的推送消息。
你的服务器提供的这个信息负载应该是JSON字典的格式。一个简单的推送信息负载应该是这样的:
{ "aps": { "alert": "Hello, world!", "sound": "default" } }
如果你不了解JSON,你只需要知道用“{}”符号分割出来的一个代码块代表了一个键与值对应的字典(和NSDictionary相似)。
我们的信息就是这样的一个字典。这个字典里至少要有一个物件,“aps”。在这里,“aps”本身又是一个字典。“aps”包含了“alert”和“sound”两个键。当设备收到这个消息时,程序应该显示一个弹出消息:“Hello,world!”并且发出标准的提示音。
你还可以在“aps”字典中加入其他物件来设置那个消息。比如:
{ "aps": { "alert": { "action-loc-key": "Open", "body": "Hello, world!" }, "badge": 2 } }
本地和推送信息编程指南.
推送信息应该很简洁;那个信息负载不应该超过256个字节。这样一般有足够的空间传送一个SMS信息或者一个推特消息。正确的服务器不会浪费宝贵的负载空间了传送换行符和空格,所以你的服务其应该发送这样的信息:
{"aps":{"alert":"Hello, world!","sound":"default"}}
这个对于人来说比较难读懂,但是却更节省空间。苹果APNS不会接受超过256个字节的推送消息。
推送消息的易错点
推送消息不可靠!
太不可靠了! 就算在APNS服务器接收了信息的情况下,推送消息也不一定会成功地传到用户设备上。
你的服务器在发射推送消息到APNS后,没有任何办法可以获取消息的状态。消息实际被推送的时间也不一定,可能几秒钟,也可能要半个小时。
并且,用户的iPhone并不是随时都能接收推送信息。他们可能在一个无法接收苹果推送服务的无线网络中,原因可能是相应的网络端口被封闭了;或者用户的手机关机了。
苹果推送服务会在手机重新上线后试着重试没有传递的信息,但是这是有时限的。时限一过,我们就永远失去那个信息提示了!
在看到苹果推送服务的账单后
好贵啊! 如果有很多用户,或者所推送的信息需要从其他地方不断获取,那这个服务的费用可能会很高。
打个比方,如果你控制了RSS源。因为你会清楚知道什么时候会有新的RSS,你可以轻松直接地在适当的时候给用户推送信息。
但是,如果你的程序允许用户输入自定义的RSS网址怎么办?在这种情况下你需要有自己的一套方案来探测是否有心的RSS信息。
事实上,你的服务器需要不停地访问这些地址来探测新的RSS信息。如果你有很多用户,那你就需要不断增加新的服务器来探测和发送信息。这很快就会变得非常昂贵。
理论问题讨论完了,那我们就开始实践吧。 但是在做有趣的事情–编程,之前, 我们还需要在iOS开发者门户网站上完成一些无聊的设置。
Provisioning Profiles以及各类证书
苹果推送服务需要一个证书!
为了使你的程序能使用推送服务,我们需要建立一个特别用来进行这个服务的provisioning profile。另外,你的服务器还需要通过SSL证书来和苹果的推送服务器建立联系。
provisioning profile和SSL证书是一一对应的,并且只有在有一个有效的App ID的情况下才能用。这是为了保证你的服务器只能将推送信息发到这一个特定的程序中,而不能发到其他任何程序。
值得注意的是,一个程序在开发时和发布时需要用不同的provisioning profile。同时也有两种服务器证书:
- 开发时. 如果你的程序是在Debug模式下运行,并且使用的是开发阶段的provisioning profile(Code Signing Identity属性的值是”iPhone Developer”),那你的服务器必须使用开发阶段的证书。
- 生产时. 程序如果已经在苹果商店上发布(Code Signing Identity属性的值是”iPhone Distribution”),那服务器必须使用生产阶段的证书。如果这两个被弄混了,推送的提示信息就无法到达你的程序。
在这个叫教程里,我们将只会使用开发时的profile和证书。
生成Certificate Signing Request(证书申请)
还记得你如何在iOS开发者网站上注册iOS开发计划并获取开发证书的吗?我们下面要做的和那个类似。但我还是建议你一步一步跟着我来完成这个操作。因为大多数问题都出在证书上。
电子证书都是基于公共和私有密钥加密的。你并不需要知道这个加密的过程,但你需要知道那个证书必须和一个私有密钥一起用才会有效。
那个证书是这组密钥的公共部分。所以把这部分随便给别人是没有问题的,在你用SSL来通讯时就需要这么做。但是那个私有密钥却需要保密。这个秘密不能让任何别人知道。如果你没有这个密钥,那证书就失效了。
在你申请一个电子证书时,你需要提交一份Certificate Signing Request(证书申请),缩写为CSR。在你生成CSR时,会生成一个新的私有密钥并被放到你电脑的keychain中。然后你把这个CSR发到一个证书的认证网站(在我们的情况下,这个网站就是苹果的开发者门户网站)。这个网站会用CSR生成相应的SSL证书。
在你的Mac上打开Keychain Access程序(在Applications/Utilities子目录下),然后在菜单中选择 Request a Certificate from a Certificate Authority…,意思是从证书权威获取证书。
WWDR Intermediate Certificate。 还需要注意的是,在Keychain Access主窗口中,千万不要选择任何的私有密钥。
然后你应该看到类似下面的窗口:
输入你的邮箱。我曾听有人说你应该用和你申请iOS开发者计划时一样的邮箱,但是应该任何邮箱都是可以的。
在Common Name(公用名)一栏输入”PushChat”. 你可以输入任何名字作为公用名,但最好选择一些有代表性的名字。这样我们以后才能很快的找到这个私有密钥。
在Saved to disk(保存到硬盘)边上打钩。将它保存为”PushChat.certSigningRequest”。
如果你现在来到Keychain Access的Keys部分,你应该看到一个新的,你刚刚生成的私有密钥。右键点击并将它汇出。
将汇出文件命名为“PushChatKey.p12”然后输入一个加密码。
为了方便,我的加密码就是“pushchat”。但为了更好地保护这个p12文件,你应该选择一些更难猜到的加密码。私有密钥必须是秘密的,不是吗?但是你一定要记得住这个加密码,否则私有密钥还是不能用。
创建App ID和获取SSL证书
登陆iOS置备门户网站.
首先,我们需要创建一个新的App ID。每一个用推送信息服务的app都需要一个特有的ID,因为只有这样,被推送的信息提示才能被发送到正确的程序上。(在这里,你不能使用通配符ID。)
在边上的目录栏中选择“App IDs”,然后点击“New App ID”按钮。
我填写内容如下:
- Description: PushChat
- Bundle Seed ID: Generate New
- Bundle Identifier: com.hollance.PushChat
你最好是选择一个你自己的Bundle Identifier,而不是用我的,例如“com.你的网站.PushChat”。你之后在Xcode中还要使用这个Bundle Identifier。
很快,我们会生成一个SSL证书。你的服务器就是用这个证书来和苹果的推送服务器建立安全连接。这个证书和你的App ID绑定在一起,这样你的服务器就只能给这一个app发送推送信息提示了。
在你创建好App ID后,你应该看到类似如下的列表:
在“Apple Push Notification service”(苹果推送信息服务)一栏里,有两个橘黄色的标志分别写着“Configurable for Development”(开发设置)和“Configurable for Production”(发布生产设置)。这意味着我们的App ID已经可以用来进行推送了,只是还需要我们做相应的设置。点击那个写着“Configure”的链接来打开设置App ID的界面。
在“Enable for Apple Push Notification service”(启用苹果推送服务)旁打钩然后点击对应“Development Push SSL Certificate”的Configure 按钮。苹果推送服务SSL证书助手界面就出现了:
它首先要你生成一个证书签署请求。我们已经完成这一步了。点击Continue。在下一步你需要上传已经生成的证书签署请求(CSR)。选择那个CSR文件,然后点击Generate。
生成SSL证书需要几分钟时间,完成后点击Continue。
点击“Download” 来下载证书 – 名字应该叫 “aps_developer_identity.cer”. 点击 “Done” 来关闭助手界面并回到设置App ID的界面。
如你所见,我们现在有一个有效的证书了,可以开始给App推送证书了。如果需要,你可以在刚才的地方重新下载证书。开发证书的有效期是3个月。
当你的App准备可以正式使用时,重复上述步骤来获取生产时用的证书。步骤是一样的。
注意: 生产时用的证书有效期是1年。但你需要提前更新它以确保你的App的推送服务不会中断。
创建PEM文件
现在我们总共有三个文件:
- CSR
- 私有密钥(PushChatKey.p12)
- SSL证书(aps_developer_identity.cer)
将这三个文件好好保存起来。你可以选择不再用那个CSR文件了。但是在你更新证书时,你可以使用同一个CSR或者获取一个新的。如果你获取新的CSR,你同时会生成一个新的私有密钥。如果使用同一个CSR,那就只有SSL证书需要改变了。
我们需要将证书和私有密钥转换成另外一个更方便的格式。我们在服务器的推送部分会用PHP来写,所以我们现在可以把证书和私有密钥合并成一个PEM格式的文件。
PEM文件是怎么运作的并不重要(说实话我也不知道),但是这样我们能更容易地在PHP里使用这个证书。如果你准备用其他语言来写这个服务器部分,那下面的步骤可能会不配套。
我们将使用命令行OpengSSL的工具来完成这项任务。打开一个Terminal并输入以下指令。
“cd”到你下载证书,密钥文件的文件夹,对于我是桌面文件夹:
$ cd /Users/matthijs/Desktop
将那个.cer文件转换成.pem文件:
$ openssl x509 -in aps_developer_identity.cer -inform der -out PushChatCert.pem
将那个密钥.p12文件转换成.pem文件:
$ openssl pkcs12 -nocerts -out PushChatKey.pem -in PushChatKey.p12 Enter Import Password: MAC verified OK Enter PEM pass phrase: Verifying - Enter PEM pass phrase:
你首先需要输入.p12文件的密码,这样openssl才能读取这个文件。然后你需要你个新的密码来对pem文件进行加密。在这个教程中我用的还是“pushchat”。但你自己应该选择一个更加保险的密码。
注意:如果你不输入PEM的密码,openssl不会给你任何的错误信息。但是生成的.pem文件里就不会有那个密钥。
最后,将那个证书和密钥合并为一个文件:
$ cat PushChatCert.pem PushChatKey.pem > ck.pem
我们应该测试一下这个证书是否能用。执行如下指令:
$ telnet gateway.sandbox.push.apple.com 2195 Trying 17.172.232.226... Connected to gateway.sandbox.push-apple.com.akadns.net. Escape character is '^]'.
我们试着与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会给出错误信息,但你可能需要在那一大窜信息中找出错误信息。
值得注意的是,APNS其实有两个不同的服务器:那个沙盒服务器使用来测试的。还有一个正式的服务器是在你的程序投入生产后使用的。我们上面用的是测试用的服务器。因为我们的证书是开发时才能用的。
生成Provisioning Profile
我们还需要用Provisioning门户网页来生成一个新的Provisioning Profile。在左边的目录栏中点击“Provisioning”,选择“Development”来生成一个新的profile。
填写表格如下:
- Profile Name: PushChat Development
- Certificates: 在你的证书旁打钩
- App ID: PushChat
- Devices: 选择你的设备
我们需要一个新的profile因为每个有推送信息的app都需要和它App ID相对应的一个profile。
点击Submit,你的profile就会被生成。那个profile的状态一开始会是“Pending”。刷新页面直到它的状态变成“Active”,这时你就可以下载那个文件了(给它取名为PushChat_Development.mobileprovision)。
双击那个文件或者将这个文件拖到Xcode的图标上,这样就能将这个profile添加到Xcode中了。
在你将程序投入生产时,你需要重复上述步骤来生成Ad Hoc或者distribution profile。
一个非常基础的APP
到现在为止我们做的准备工作都比较枯燥,但却是必要的。我们详细的学习了生成各种证书的过程因为我们不会经常坐这件事,但是推送信息没有这些就无法运作。
通过连接到沙盒服务器,我们确认了证书是可用的。现在我们要测试一下我们是否确实可以推送提示信息!
启动Xcode并建立一个新的项目。 选择View-based Application作为这个项目的模版然后点击进入下一步。
我是这样填写各个空格的:
- Product Name: PushChat
- Company Identifier: com.hollance
- Device Family: iPhone
那个Product Name(产品名字)和Company Identifier(公司代码)合在一起就是Bundle ID。我的是“com.hollance.PushChat”。 你应该选择和你在苹果的Provisioning Portal上注册的App ID相对应的产品名字和公司代码(com.yourname.PushChat)。
完成创建项目并打开PushChatAppDelegate.m文件。将didFinishLaunchingWithOptions改为如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; // 让手机知道我们想接收推送信息提示。 [[UIApplication sharedApplication] registerForRemoteNotificationTypes: (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)]; return YES; } |
我们调用registerForRemoteNotificationTypes方法来告诉OS我们的程序想接收推送信息。
编译并运行你的程序。你应该在真正的苹果设备上运行因为模拟器不支持推送信息提示。Xcode应该会自动选择那个新的provisioning profile。如果你得到一个关于“code sign”的错误,那你需要确保已经选择了正确的profile。 你可以在“build settings”中的“Code Sign”设置部分对其修改。
当启动你的程序时,系统应该会弹出一个选择菜单来询问用户是否允许当前程序推送提示信息。
你的程序只会向用户询问一次然后记住用户的选择。用户只有选择“OK”之后你的程序才能接收推送提示信息。用户也可以在iPhone系统设置菜单中更改推送信息的设置。
你app的名称会出现在手机的提示设置界面里。用户可以在这里设置是否允许你的app推送信息提示,显示数量图标以及发出提示音。
你的app也可以通过如下的代码来探测用户启用了哪一种提示方式:
UIRemoteNotificationType enabledTypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes]; |
要让我们的app开始接收推送提示,我们还需要将下面的代码加入PushChatAppDelegate.m文件中:
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken { NSLog(@"My token is: %@", deviceToken); } - (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error { NSLog(@"Failed to get token, error: %@", error); } |
当你的app注册远程推送提示时,它会获取一个设备代码(device toke)。这是一个32字节的独特数字来辨别用户的设备。你可以把这个代码看做是用户设备的地址。APNS用这个地址来推送信息提示。
在你用来测试的苹果设备上运行你的app。你应该在Xcode的控制台窗口中看到:
My token is:
那个代码是一个NSDatat对象,拥有某种二进制数据结构。我们不应该对它进行任何修改。我们只用知道它有32个字节,可以用64个十六进制的字符表示。我们会直接用这种表示方式(去掉那括弧和中间的空格)。
如果你在模拟器里运行你的程序,didFailToRegisterForRemoteNotificationsWithError:方法应该会被调用因为模拟器不支持推送信息提示。
我们的app就暂时写完了。但要真的开始推送信息,我们还需要完成最后的一步!
推送第一个信息提示
我曾说过,你需要有一个服务器来把信息推送到你的app上。但为了测试,我们还不需要一个服务器。你可以用下面这个简单的PHP脚本来和APNS建立连接并将信息推送到指定的设备上。你可以直接在你的Mac上运行这个脚本。
下载这个叫“SimplePush”代码包 并解压缩. 你需要对simplepush.php文件进行如下修改.
// 把你的设备代码放在这里(不要空格): $deviceToken = '0f744707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bbad78'; // 把你密钥的密码放在这里: $passphrase = 'pushchat'; // 把你的提示信息放在这里: $message = 'My first push notification!'; |
你应该把设备代码从app里拷贝粘贴到$deviceToken变量。请确保你去掉空格和括弧。把$passphrase的值设为你密钥的密码, $message的值设为你要推送的信息字符串。
把你的ck.pem文件拷贝到SimplePush文件夹中。记住,ck.pem包含你的证书和密钥。
打开终端并打入:
$ php simplepush.php
如果一切顺利,那个脚本应该回应:
Connected to APNS Message successfully delivered
然后几秒之后,你应该收到app的第一个推送信息提示:
注意,如果你的app是开着的,你就看不到推送的信息。这是因为我们还没有写代码在app里接收推送的信息。你应该关掉app然后重试。
如果simplepush.php脚本因错误退出,那你因该确保你的PEM文件是正确生成的,并且确保你可以连接到苹果的沙盒服务器(请看上面的详细步骤)。
我们并不用急着知道这个脚本到底做了什么,因为这是这个教程的下一部分的内容。在下一部分,我们将学习如何编写一个真正的推送服务器。
下一步做什么?
到现在为止,你的app已经可以接收推送信息了,你也成功地用PHP代码推送了第一个信息!
下一步是看看这个教程的第2部分。我们会学习编写一个简单的短信app叫做PushChat,并运用推送的方式来传递信息。我们还会学习如何完成一个完整的服务器API,在后台不断地推送提示信息!
如果你对这个教程有任何问题,评论和提示,请加入下面的论坛讨论!
这个教程是由iOS教程组成员 Matthijs Hollemans编写的。Matthijs是一个经验丰富的*iOS 开发者,可以接收合同工作!
Tweet