【iOS开发者】APP Push推送通知服务教程 (1/2)

前面刚刚更新提醒各位开发者《请不要滥用Push Notification功能!》,小编发现dApps根本就没有推送的教程,这不是一个专业的开发者社区,为此,需要补充。

这是由iOS教程组成员Matthijs Hollemans撰写的一篇有关推送通知的教程,Matthijs Hollemans是一位有着丰富经验的iOS开发者和设计者。
在iOS系统里,应用程序在后台运行的时候有很多事情都做不了(译者注:由于沙盒限制)。应用程序只允许在这个“沙盒”内做一些操作,这样可以减缓电池的消耗,延长电池的使用时间。
但是,假如用户现在没有在用你的程序,而你的程序恰好发生了一些有趣的事情,而你也想让用户知道,这时你该怎么做?
例如,用户收到了一条tweet的新消息,他们喜欢的队伍赢得了比赛,或者晚饭已经准备好了。因为应用现在没有在运行,程序就接收不到这些新消息。
幸运的是,苹果公司为这种情况提供了一个解决办法。你可以写一个服务器端的组件来解决这样的问题,而不是让应用不断的在后台运作,检查事件的发生。当一些有趣的事件发生的时候,服务器端的组件可以发送一个推送通知!
下面是推送通知可以做的三件事:

  • 显示一条短信息
  • 播放一个简短的提示声音
  • 在程序的图标上设置推送消息数量

你可以根据需要,随意组合以上三项功能;例如,你可以播放简短的提示声音和设置应用图标上推送消息的未读数量,但不显示消息。
在这两部分的系列教程里,你将通过创建一个简单使用APNS(苹果的推送通知)的app,来实现推送通知。
首先,在第一部分教程里面,你将学习怎么设置应用程序,来接受推送通知和一条测试消息。
这篇教程是为中高级iOS开发者写的。如果你只是一个iOS开发的初学者,应该先学习这个网站的其他教程。所以,对于初学者,强力推荐先学习下面的两篇教程:

  • 如何为iOS应用编写一个简单PHP/MySQL服务程序(待补充)
  • 如何在ios应用里面使用web service(待补充)

闲话少说,让我们把完成它!
简要概述
想让应用程序的推送功能正常工作,你需要相当多的努力,这个过程非常繁琐。以下是这个过程的概述图:

  1. 应用程序需要激活推送通知功能。在使用之前,用户需要确认他是否愿意接受这些推送功能。
  2. 应用程序接到一个“device token”。你可以认为“device token”是推送通知发送信息的目的地址。
  3. 应用程序会将“device token”发送给你的服务器
  4. 当你的应用程序发生了有趣的事情,你的服务器向 “苹果推送通知的服务器(Apple Push Notification Service,缩写,APSN)”发送通知。
  5. APSN再向用户的设备(例如,iPhone,iPad等)发送通知。

当用户的设备接受到推送通知,将会显示提醒框,播放提示声音,更新图标的未读信息数目。用户可以通过点击提醒框来加载应用程序,苹果公司给推送通知加入了可选内容,你可以根据需求来操作推送通知。
当iOS4实现了本地通知和多任务,苹果的推送通知是否还有使用价值?当然有!
本地通知被定时事件调度限制,并且只有VOIP、导航、后台音频播放这些应用在后台没有被限制。如果当应用程序处于关闭状态时,你想将外部事件的发生通知给你的应用程序用户,仍然需要推送通知。
在这篇教程中,我将解释推送通知系统实现的细节以及如何在应用中建立推送。这里有很多要解释的,这需要(您)花点儿时间去明白。

你需要为推送做些什么

在你的应用中添加推送通知,你需要:
一台iPhone或者iPad 推送通知不能在模拟器上实现,所以你需要在设备上测试。
一个iOS开发者证书 你需要一个新的AppID和每个应用程序使用的推送证书,推送服务器需要的“SSL ”证书,你可在iOS开发网站上做这些。
如果你想跟随这篇教程的例子学习,你将需要创建你自己的推送证书和SSL证书,你不可以使用我的这些证书。因为获得正确的证书是很重要的,我将详细解释如何得到一个推送证书。
一个联网的服务器 推送通知通常是由服务器来发送的。对于开发,你可以使用一个Mac来代替服务器(我们将在教程中这样做),但是发布的产品,你至少需要类似VPS( Virtual Private Server )的服务器。
一个廉价的共享虚拟主机账户不能满足发布产品的需要。你需要在服务器的后台启动一个进程,安装一个SSL证书,并且能够在某个端口中能够外联TLS。
大部分的共享虚拟主机并不让你实现这些功能,即使如果申请的这些需求通过了。无论怎么样,我真的建议你使用一个VPS主机(相关VPS主机推荐由给券网提供)。

解析一个推送通知

你的服务器负责创建一个推送通知消息,所以了解一个推送通知消息的构成是有必要的。
一个推送通知是一个短信息,由“device token”,“payload–负载内容”,和其他的一些字节组成。“payload–负载内容”是我们感兴趣的部分,因为它包含着我们实际想发送出去的数据。
你的服务器必须提供“payload–负载内容”,它是以JSON的字典的数据格式来组织数据的。下面是一个很简单的推送消息payload:
{
"aps":
{
"alert": "Hello, world!",
"sound": "default"
}
}

对于不了解JSON的人,一个block的划分由一对花括号“{}”包裹,其中包含一个由“键/值 (key/value)”对组成的“字典”,(就像NSDictionary)。
“payload–负载内容”就是一个“字典”,包含了至少一个“aps”项,“aps”本身也是一个“字典”。在我们的例子中,“aps”包含“alert”和“sound”字段。当这个推送通知被接收后,它将显示一个包含“Hello, world!”内容的提醒框,并且播放标准的提示音。
你可以向“aps”添加另外的选项,来配置通知,例如
{
"aps":
{
"alert":
{
"action-loc-key": "Open",
"body": "Hello, world!"
},
"badge": 2
}
}

现在“alert”是一个字典。”action-loc-key” 对应的value替代了“View”按钮上的文本内容,”badge”字段包含的数字将被显示在应用图标上,这个通知不会播放提示音。
有很多途径去设置JSON的“payload–负载”内容,你可以改变播放声音,你可以提供本地化的文本,并且添加自己的字段。更多信息,请详见官方文档Local and Push Notification Programming Guide
推送通知的目标就是精短;“payload–负载内容”的大小不能超过256个字节。这样留给你的空间和一条短消息或者一个tweet消息的大小一样。一个小型的推送服务不会在换行符和空格上浪费空间,这样一条推送就像下面所显示的:
{"aps":{"alert":"Hello, world!","sound":"default"}}
上面这条消息可读性比较低,但是它节省了足够的字节,所以牺牲可读性是值得的。如果一个推送通知的payload超过了256个字节,那么这个推送就不会被“APNS”接受。

关于推送通知常见的错误

上面这条消息可读性比较低,但是它节省了足够的字节,所以牺牲可读性是值得的。如果一个推送通知的payload超过了256个字节,那么这个推送就不会被“APNS”接受。

关于推送通知常见的错误


推送通知是不可靠的!即使APNS(Apple Push Notification Service苹果推送通知服务)服务器接受了推送通知,仍然无法保证该通知最终会被送达。
就你的服务器而言,推送通知会被发出并且遗忘掉;当你将通知发送到APNS后,没有办法查出它所处的状态。通知送达的时间也从几秒到半小时不等。
同样,用户的iPhone不是所有时间都可以收到推送通知。比如,因为指定的端口被封,手机处于一个不允许和APNS连接的WIFI网络。或者手机已经关机了。
APNS将会在手机连接到可用网络后下发从该机器收到的最后一条通知,但是只会尝试有限的时间。一旦发送超时,此条通知就会永远丢失!

推送通知会使开销很大!如果你掌控了需要推送的内容,在你的应用中加入推送功能相当容易和廉价,但是当你有好多用户和数据需要轮询的时候,这个功能就会使得服务器开销庞大(译者注:不一定指价格,包括是资源消耗)。
比如,你打算在自己的RSS订阅更新的时候通知用户,这样做没问题。因为你控制着RSS订阅,并且知道它何时发生变化——当你更新网站内容时——于是你的服务器可以在合适的时候发出通知。
但是如果你的应用是一款RSS阅读器,允许用户添加自定义的链接呢?这种情况下,你需要构建一些机制去检测那些订阅的更新。
实际上,这意味着你的服务器为了检查那些订阅的变化,需要不停的轮询它们。如果你拥有很多用户,你可能不得不安装一堆新服务器去处理这些工作和满足带宽需求。对于这类应用,推送会变得异常昂贵,可能不值得去做。
好了,理论上足够了。下面到时间来学习如何使用推送了。在我们投入到美好的事物——编程!之前——有一些无聊的搭建环境的工作需要在iOS Provisioning Portal上完成,所以让我们尽快完成它。

Provisioning Profiles和证书,天哪!


在你的应用中使用推送通知,需要用一个配置过推送功能的provisioning profile来签名。此外,你服务器所有与APNS的通讯都需要进行SSL证书签名。
Provisioning profile和SSL证书紧密联系在一起,并且只对一个App ID有效。这个保护措施可以保证只有你的服务器可以发推送通知到你的应用,其它服务器不可以。
正如你所知道的,应用程序在开发和发布阶段使用不同的provisioning profiles。同样,推送服务器的证书也有两种:

  • Development. 如果你的应用程序运行在debug模式,并且使用的是Development provisioning profile (Code Signing Identity 是 “iPhone Developer”)签名的,你的服务器必须使用Development证书。
  • Production. 使用Ad Hoc方式发布的,或发布在App Store(Code Signing Identify 是 “iPhone Distribution”)上的应用程序,必须和使用Production证书签名的服务器通讯。如果这里面有不匹配,推送通知将无法送达你的应用。

在这篇教程里,我们不需要为分发profiles和证书烦恼,只需要使用Development版本的即可。

生成证书签名请求(Certificate Signing Request, CSR)

还记得你在注册成为iOS开发者之后,如何去iOS Provisioning Portal生成一个开发证书吗?如果记得,下面的步骤应该会比较熟悉。不过,我仍然建议你准确地按照步骤来做。因为大多数在实现推送通知过程中遇到的问题,都是由于证书问题引起的。
数字证书基于公钥-私钥加密方法。你不需要知道任何关于证书的加密方法,但是你要知道证书一直会与一个私钥搭配使用。
证书是密钥对的非秘密的部分。将它发送给其它人是安全的,比如通过SSL通讯的过程中就会包含证书。然而,对于私钥,当然是私有的。它是秘密的。你的私钥只对你有用,对其他人没用。要重视的是:如果你没有私钥的话,就无法使用证书。
每当申请一个数字证书的时候,你需要提供一个证书签名请求,简称CSR。当你创建了CSR后,会生成一个新的私钥保存到keychain应用程序中。然后你将CSR发送到一个证书颁发机构(目前情况下就是iOS Developer Portal),它会根据CSR中的信息生成SSL证书。
打开Mac中的Keychain Access程序(在Applications/Utilities下),选择菜单中的Request a Certificate from a Certificate Authority…

如果菜单中没有“Request a Certificate from a Certificate Authority with key”选项,就先去下载安装WWDR Intermediate Certificate。并且确认Keychain Access窗口里没有私钥被选中。
现在,你应该会看到下面的窗口:

在里面输入你的邮件地址,听有人推荐说最好使用和注册IOS开发者证号同样的邮件地址,但看起来任何邮件地址都可以。在Common Name中输入“PushChat”。你可以输入任何字符串,但最好是有意义的。这会使你以后容易查找这个私钥。
选择Saved to disk选项,点击Continue。将文件保存为“PushChat.certSigningRequest”。
如果你切换到Keychain Access软件的Keys标签,你将会看到一个新的私钥出现在你的钥匙串里。右键点击它,选择Export。

将私钥保存为“PushChatKey.p12”,输入一个密码短语。
为了教程的方便,我用了密码短语“pushchat”来保护这个p12文件,但是你应该选择一些更不容易被猜出的。记住,私钥是要保密的。另外,一定要选择你能记住的密码短语,否则以后就无法使用这个密钥了。

创建APP ID和SSL证书

登录iOS Provisioning Portal.
首先,我们要创建一个新的应用程序ID。每个要推送的APP需要自己第一无二的ID,因为推送通知要发送到一个特定的应用程序。 (注意不要使用通配符ID)。

需要填写的字段

  • 描述: PushChat
  • Bundle Seed ID: Generate New (默认选项)
  • Bundle Identifier: com.hollance.PushChat

在这里选择自己的Bundle Identifier,– com.yoursite.PushChat – (注意替换下,不要跟着教程一样哦)
过段时间后,我们需要创建SSL证书来供推送时使用,证书与APP ID相连接,推送通知将只能从服务器发送到特别APP。
如果你创建了一个APP,并且有了ID,那么应该如下图所示:

在“Apple Push Notification service” 这一栏,有两个橙色的灯,【Configurable for Development】 和【Configurable for Production】,这就意味着这个应用ID已经准备好了,接下来设置相关的选项。点击【Configure】链接。

点选【Enable for Apple Push Notification service】框,点击对应【Development Push SSL Certificate】的设置按钮 【Configure】,弹出 苹果推送服务SSL证书助理(Apple Push Notification service SSL Certificate Assistant)窗口 :

首先提示你准备好证书签名申请文件,我们上面已经准备好了,点击继续,在下一步中进行上传CSR文件的操作,选中之前生成的CSR文件然后点击生成(Generate)。

生成证书需要几秒钟,接着点继续按钮(Continue)。

下载生成的证书并保存为“aps_developer_identity.cer”。点击完成按钮(Done)关闭助理窗口返回APP ID界面。

你可以看到,我们已经激活开发用的证书认证,如果需要你可以在这里重新下载证书,开发证书的有效期为3个月。
等你要正式发布你的应用的时候,你必须在开发认证那一项目下把这个过程重新来一遍。
备注:开发认证的证书有效期为一年,你必须在到期前重新生成一次来保证你的系统正常运行。
在本例中你不需要安装这个证书,如果你双击文件进行了安装,你可以在钥匙串访问程序中查找到,这个证书已经和私钥绑定在一起了。
在侧边栏的应用程序的ID,然后点击新的应用程序ID“按钮。

制作PEM文件

现在我们有三个文件:

  • 认证签名申请文件(CSR)
  • 私钥文件(PushChatKey.p12)
  • SSL证书文件(aps_developer_identity.cer)

妥善地保存好这三个文件,特别是CSR文件,在你的证书失效之后,你可能会再次用到它来申请证书。原来的私钥还可以用,只有SSL证书文件是新的。
我们需要转换证书和私钥文件为一种常用格式,示例中我们使用PHP开发服务组件,我们需要把证书和私钥文件合并为PEM格式文件。
我们不用关心是哪一种具体的PEM编码格式(实际上我也不是很清楚),关键是PHP可以用来与服务器建立有效的网络连接,其他的编程语言可能会用到其它格式的文件。
在这里,我们用MAC电脑的命令行工具OpenSSL来操作,打开一个“终端”(Terminal)程序:
通过cd命令转到存放三个文件的文件夹,我这里的操作是:
$ cd /Users/matthijs/Desktop
转换证书 .cer 文件到 .pem 文件格式:
$ openssl x509 -in aps_developer_identity.cer -inform der -outPushChatCert.pem
转换私钥 .p12 文件 到 .pem 文件格式:
$ openssl pkcs12 -nocerts -outPushChatKey.pem -inPushChatKey.p12
EnterImportPassword:
MAC verified OK
Enter PEM pass phrase:Verifying-Enter PEM pass phrase:

在这里,你首先要输入私钥文件的口令以便访问.p12文件,接着要求输入一次新的口令来加密新的PEM文件,这里我们都用“pushchat” ,你也可以使用更复杂的口令来保护你的私钥。
备注:如果你不输入一个6位以上的加密口令,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的让你知道的引擎盖下工作的过程。
如果连接成功,你可以输入几个字符,当你按下回车,连接就断开了,连接失败的话也会有提示信息。
特别提示你有两个不用的APNS服务器,一个用于测试的沙盒服务器,一个用于正式使用的服务器,我们制作的证书是用于测试的,所以上面的示例中我们使用的是沙盒服务器的地址。

准备配置文件

iOS Provisioning Portal操作还没有完,点击左项菜单上的【Provisioning】,点击【New Profile】新建一个配置文件。

填入如下内容:

  • 配置名称Profile Name: PushChat Development
  • 证书(Certificates):选中你的开发者证书
  • 应用ID(App ID): PushChat
  • 设备(Devices): 选中你的开发设备

这里的操作跟你之前的没有什么不用,只是具备推送功能的应用需要一个新配置文件来跟设置好的APP对应起来。
点击发送按钮(Submit),新的配置文件就会重新生成,稍等片刻,再刷新页面即可下载新的配置文件(PushChat_Development.mobileprovision)。
将配置文件下载并加载到XCode(双击或拖到XCode图标上)。
准备正式发布前,你也要这样操作来准备用发布用的配置文件。

简单的示例应用

你还在吗?经历了这么多终于可以来点兴奋点的事情了,不过上面的这些过程是必不可少的,至少这些也不需要天天搞的,否则是会死人的,不是吗?
我们已经实现与沙盒服务器的加密网络连接,现在就让我们来实现推送通知的发送吧。
打开你的XCode,选择【File】【New Project】,选择【View-based Application】点击继续:

填入以下内容:

  • Product Name: PushChat
  • Company Identifier: com.hollance
  • Device Family: iPhone

根据你的实际情况填入应用名称和开发者标识,这里我们就填入“com.hollance.PushChat”。你应该填入跟你在开发者门户里填入的一致信息(com.yourname.PushChat)。
完成新建项目的操作,打开PushChatAppDelegate.m,修改 didFinishLaunchingWithOptions 过程:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
// Let the device know we want to receive push notifications
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
return YES;
}

调用registerForRemoteNotificationTypes 通知系统应用是需要接收推送信息的。
在你的设备上编译运行应用,模拟器是不支持推送信息的。XCode应该会自动选择配置文件,如果出现签名错误,你需要在 Code Signing build settings手动选择之前下载的配置文件。应用启动时会注册推送通知服务,弹出下面的确认窗口提示用户允许此应用接收推送通知服务。

应用只会提示询问一次,如果用户选择接受,设备就一切就绪了。如果用户选择了拒绝,应用将永远无法接收到信息,用户可以在设备的设置项目中修改此项设定。

应用的名称将会添加到设置程序中的通知项目下,用户可以方便地在这里开启或关闭或自定义接收信息的种类和方式。

应用也可以通过程序来激活具体的提示方式:
UIRemoteNotificationType enabledTypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
还有额外的一件事,为了发送信息到指定的手机,我们还需要一些操作:
- (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);
}

当应用注册推送服务成功时,就可以获取用户设备识别ID(Token ID),这是对应你的设备一个32位的唯一编码,你可以理解为推送信息的地址。
运行应用,在XCode的终端窗口你可以看到以下信息:
My token is:
<740f4707 bebcf74f 9b7c25d4 8e335894 5f6aa01d a5ddb387 462c7eaf 61bb78ad>

Token ID是加密的数据结构,储备在NSData对象中。这里你知道它是32位长度就够了,上面你看到的是64个16进制的字符,我们将使用这个格式,当然<>和空格要过滤掉。
在模拟器中运行,didFailToRegisterForRemoteNotificationsWithError会返回错误:method will be called as push notifications are not supported in the simulator.
应用准备好了,就差最后一件事了。

发送我们的推送通知

之前我们都谈到要有服务器或服务组件来实现推送通知的发送和管理,在这里,我们先不急着搭建服务器,这里有一个简单的PHP脚本用来建立到APNS的连接和发送测试信息到之前的设备上。
你可以在MAC上直接使用:下载SimplePush code代码,解压后,修改simplepush.php中的以下几个地方:
// Put your device token here (without spaces):
$deviceToken = '0f744707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bbad78';
// Put your private key's passphrase here:
$passphrase = 'pushchat';
// Put your alert message here:
$message = 'My first push notification!';

复制设备识别ID到变量$deviceToken,别留下任何一个空格,完完全全就是64个16进制字符;指定私钥的口令和要发送的信息内容;复制ck.pem到脚本所在文件夹,ck.pem包括了证书和私钥。
开启终端程序( Terminal):
$ php simplepush.php
如果一切OK的话,脚本将返回:
Connected to APNS
Message successfully delivered

几秒钟内,你应该可以在设备上收到推送的信息了,如下图:

注意如果应用在开启运行状态的话,你看不到任何信息,信息被直接发送给应用本身,但是我们还没有通过编程来处理收到的信息,不信你可以再试一下。
如果PHP脚本退出并返回错误信息,请检查PEM文件是否正确、连接沙盒服务器是否正常。
PHP脚本具体的实现过程就不讨论了,有兴趣的或需要自行搭建服务器来管理发送推送通知的可以看此教程的下篇(请找原出处)。

总结

现在你已经成功地实现了应用的推送通知服务,并通过自定义PHP代码delievered你的第一个推送通知!在此教程的下篇中,我们来开发一个简单的短消息应用(PushChat)来实现用户之间的推送通知功能。还有完整用于在后台不间断提供推送通知服务的服务组件API。
谢谢您的收看,哈哈。
附:教程中提供吧的simplePush PHP代码备用下载
如果文章提及的simplePush代码无法下载,请点下面的备用下载链接
[dl href=’http://pan.baidu.com/share/link?shareid=4458&uk=2502824232′]simplePush备用下载链接[/dl]