基于 OS X 和 iOS 的应用程序和框架,用于与苹果推送通知服务 (APNs) 交互
使用 Homebrew cask 安装 Mac 应用程序
brew cask install pusher
或下载最新的 Pusher.app
二进制文件
或者,您可以使用 CocoaPods 将 NWPusher 作为框架包含在内
pod 'NWPusher', '~> 0.7.0'
或者简单地包含您需要的源文件。NWPusher 采用模块化架构且没有外部依赖,所以您可以使用您喜欢的任何内容。
对您的 iOS 或 Mac 应用程序进行推送通知测试可能很痛苦。您可能想要设置自己的服务器或使用市场上许多推送 Web 服务之一。不管哪种方式,正确连接所有这些系统都需要大量工作。当一切正常工作时,推送通知会非常迅速(小于 1 秒)且可靠。然而当没有任何推送通知到来时,可能很难找出原因。
这就是我制作 Pusher 的原因。它是一个 Mac 和 iPhone 应用程序,用于直接将推送通知发送到 苹果推送通知服务。无需设置服务器或在线创建账号。您只需要 SSL 证书和设备令牌,就可以开始从您的 Mac 上直接推送,甚至可以从 iPhone 上直接推送!Pusher 提供详细的错误报告和记录,这在对您设置的验证非常有帮助。
Pusher 包含一个用于 OS X 和 iOS 的小型框架。它提供用于程序化发送通知的各种工具。在 OS X 上,它可以使用密钥链检索推送证书和密钥。Pusher 还可以使用 PKCS #12 文件(.p12)而不使用密钥链。如果您想更好地了解推送通知是如何工作的,那么这个框架是一个很好的起点,可以进行一些实验。
Mac OS X 应用程序,通过 APN 服务发送推送通知
OS X 和 iOS 框架,用于从您的应用程序发送推送
在开始发送推送通知数据包之前,有一些难题需要克服。首先,您需要获取要发送通知的应用的Apple 推送服务 SSL 证书。此证书由 Pusher 用于设置 SSL 连接,通过该连接将发送数据包到 Apple。
其次,您还需要获取要发送数据包的设备的设备令牌。每个设备都有其唯一标识符,只能从应用内部获得。这有些复杂,但最终只需要在 Apple 的开发者中心网站上点击几下,耐心等待即可。
让我们从 SSL 证书开始。目标是获取证书和私钥并放入您的 OS X 密钥链中。如果其他人已经生成了此证书,您需要请求将这些证书导出为 PKCS12 文件。如果没有生成证书,您可以在以下步骤中生成证书和私钥。
请注意,您将最终下载一个证书,需要将其与私钥一起安装到密钥链中。它看起来应该像这样
注意:有 开发
和 生产
证书,它们应该(通常)分别对应于您的应用的 DEBUG
和 RELEASE
版本。请确保您获取了正确的证书,检查 开发(沙盒)或生产,iOS 或 Mac,以及 捆绑标识符。
推送证书应导出为 PKCS12 文件,这允许您与同行开发者共享这些文件。
现在您需要获取一个设备令牌,这是一个 64 位的十六进制字符串(256 位)。您需要从要发送推送的 iOS 应用内部进行。将以下行添加到应用代理中(需要 Xcode 6)
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) {
NSLog(@"Requesting permission for push notifications..."); // iOS 8
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:
UIUserNotificationTypeAlert | UIUserNotificationTypeBadge |
UIUserNotificationTypeSound categories:nil];
[UIApplication.sharedApplication registerUserNotificationSettings:settings];
} else {
NSLog(@"Registering device for push notifications..."); // iOS 7 and earlier
[UIApplication.sharedApplication registerForRemoteNotificationTypes:
UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeSound];
}
return YES;
}
- (void)application:(UIApplication *)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings *)settings
{
NSLog(@"Registering device for push notifications..."); // iOS 8
[application registerForRemoteNotifications];
}
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)token
{
NSLog(@"Registration successful, bundle identifier: %@, mode: %@, device token: %@",
[NSBundle.mainBundle bundleIdentifier], [self modeString], token);
}
- (void)application:(UIApplication *)application
didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
NSLog(@"Failed to register: %@", error);
}
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier
forRemoteNotification:(NSDictionary *)notification completionHandler:(void(^)())completionHandler
{
NSLog(@"Received push notification: %@, identifier: %@", notification, identifier); // iOS 8
completionHandler();
}
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)notification
{
NSLog(@"Received push notification: %@", notification); // iOS 7 and earlier
}
- (NSString *)modeString
{
#if DEBUG
return @"Development (sandbox)";
#else
return @"Production";
#endif
}
现在,当您运行应用程序时,64 位推送字符串将被记录到控制台。
在将 SSL 证书和私钥放入密钥链中,并拥有设备令牌后,您就可以发送推送通知了。让我们先通过 Mac OS X Pusher 应用 发送通知。打开 Pusher Xcode 项目并运行 PusherMac 目标。
顶部组合框列出了密钥链中可用的 SSL 证书。选择您想要使用的证书,粘贴要推送设备的设备令牌。下面的文本框显示了您要发送的 JSON 格式的数据包文本。有关此格式的更多信息,请参阅 Apple 文档中的 Apple 推送通知服务。
在按 推送 之前,确保您要 推送到的 应用处于 后台,例如通过按主页按钮。这样,您就可以确保应用不会干扰消息。按推送,等待几秒钟,您应该会看到通知进入。
如果一切正常的话,您可以查看下面的 故障排除 部分。
最好的体验当然是直接从一部iPhone推送到另一部iPhone。这可以通过Pusher iOS应用程序来完成。在运行PusherTouch目标之前,请确保在应用程序中包含了证书、私钥和设备令牌。将您之前导出的PKCS12文件包含到PusherTouch捆绑包中。然后转到NWAppDelegate.m
,配置pkcs12FileName
、pkcs12Password
和deviceToken
。现在运行PusherTouch目标。
如果一切设置正确,您只需连接和推送。然后您应该会在设备上收到测试..
推送消息。
再次,如果事情并不像预期的那样运作,请查看下方的故障排除部分或提交一个在GitHub上。
有关APNs架构的更多信息,请参阅Apple的文档: Apple推送通知服务
Pusher还可以作为一个框架来程序化地发送通知。包含的Xcode项目为OS X和iOS提供了示例。通过CocoaPods包含NWPusher是迄今为止最简单的方法。
pod 'NWPusher', '~> 0.7.0'
CocoaPods还会编译文档,这些文档可以通过CocoaDocs访问。或者,您也可以从Classes
文件夹中仅包含所需的文件。确保您与Foundation.framework
和Security.framework
链接。
在发送任何通知之前,您首先需要创建一个连接。在此连接建立后,可以发送任意数量的有效载荷。
请注意,Apple不喜欢您为每个推送创建连接。因此,请尽可能地重用连接以防止Apple从中阻止。
从PKCS12 (.p12)文件直接创建连接
NSURL *url = [NSBundle.mainBundle URLForResource:@"pusher.p12" withExtension:nil];
NSData *pkcs12 = [NSData dataWithContentsOfURL:url];
NSError *error = nil;
NWPusher *pusher = [NWPusher connectWithPKCS12Data:pkcs12 password:@"pa$$word" error:&error];
if (pusher) {
NSLog(@"Connected to APNs");
} else {
NSLog(@"Unable to connect: %@", error);
}
当推送器成功连接后,给您设备发送一个有效载荷
NSString *payload = @"{\"aps\":{\"alert\":\"Testing..\"}}";
NSString *token = @"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";
NSError *error = nil;
BOOL pushed = [pusher pushPayload:payload token:token identifier:rand() error:&error];
if (pushed) {
NSLog(@"Pushed to APNs");
} else {
NSLog(@"Unable to push: %@", error);
}
一两秒钟后,我们可以查看通知是否被Apple接受
NSUInteger identifier = 0;
NSError *apnError = nil;
NSError *error = nil;
BOOL read = [pusher readFailedIdentifier:&identifier apnError:&apnError error:&error];
if (read && apnError) {
NSLog(@"Notification with identifier %i rejected: %@", (int)identifier, apnError);
} else if (read) {
NSLog(@"Read and none failed");
} else {
NSLog(@"Unable to read: %@", error);
}
或者,在OS X上,您也可以使用密钥存储(keychain)来获取SSL证书。在这种情况下,首先收集所有证书
NSError *error = nil;
NSArray *certificates = [NWSecTools keychainCertificatesWithError:&error];
if (certificates) {
NSLog(@"Loaded %i certificates", (int)certificates.count);
} else {
NSLog(@"Unable to access keychain: %@", error);
}
选择正确的证书后,从密钥存储中获取身份
NSError *error = nil;
NWIdentityRef identity = [NWSecTools keychainIdentityWithCertificate:certificate error:&error];
if (identity) {
NSLog(@"Loaded identity: %@", [NWSecTools inspectIdentity:identity]);
} else {
NSLog(@"Unable to create identity: %@", error);
}
查看示例项目以了解此方法的变体。
有关客户端/服务器通信的更多信息,请参阅Apple的文档:服务提供商通信
反馈服务是Apple推送通知服务的一部分。反馈服务基本是一个包含失效设备令牌的列表。Apple建议您每天从反馈服务中读取一次,并且不再向列表中的设备发送通知。请注意,这可以用来找出谁将您的应用从其设备上删除了。
可以使用NWPushFeedback
类与反馈服务进行通信。首先使用一种connect
方法进行连接
NSURL *url = [NSBundle.mainBundle URLForResource:@"pusher.p12" withExtension:nil];
NSData *pkcs12 = [NSData dataWithContentsOfURL:url];
NSError *error = nil;
NWPushFeedback *feedback = [NWPushFeedback connectWithPKCS12Data:pkcs12 password:@"pa$$word" error:&error];
if (feedback) {
NSLog(@"Connected to feedback service");
} else {
NSLog(@"Unable to connect to feedback service: %@", error);
}
连接后,读取无效设备令牌和失效日期
NSError *error = nil;
NSArray *pairs = [feedback readTokenDatePairsWithMax:100 error:&error];
if (pairs) {
NSLog(@"Read token-date pairs: %@", pairs);
} else {
NSLog(@"Unable to read feedback: %@", error);
}
Apple在读取最后一个设备令牌后关闭连接。
在macOS上,您可以通过调用NSApplication
对象中的registerForRemoteNotificationTypes:
方法来获取您的app的设备token。建议您在启动时作为正常启动序列的一部分来调用此方法。当您的app第一次调用此方法时,app对象会向APNs请求token。在初始调用之后,当设备token发生变化时,app对象才会联系APNs;否则,它会快速返回现有的token。
当成功或失败检索设备token时,app对象会异步地通知它的代理。您使用这些代理回调来处理设备token或处理出现的任何错误。您必须实现以下代理方法来跟踪注册是否成功
application:didRegisterForRemoteNotificationsWithDeviceToken:
来接收设备token并将其转发到您的服务器。application:didFailToRegisterForRemoteNotificationsWithError:
来响应错误。注意:如果您的app运行时设备token发生变化,app对象将再次调用适当的代理方法来通知您变化。
app代理在常规启动时调用registerForRemoteNotificationTypes:
方法,传入您打算使用的交互类型。在收到设备token后,使用自定义方法将application:didRegisterForRemoteNotificationsWithDeviceToken:
方法转发到与app关联的服务器。如果注册过程中出现错误,app将暂时禁用与远程通知相关的任何功能。当接收到有效的设备token时,这些功能将重新启用。
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
// Configure the user interactions first.
[self configureUserInteractions];
[NSApp registerForRemoteNotificationTypes:(NSRemoteNotificationTypeAlert | NSRemoteNotificationTypeSound)];
}
- (void)application:(NSApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// Forward the token to your server.
[self forwardTokenToServer:deviceToken];
}
- (void)application:(NSApplication *)application
didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(@"Remote notification support is unavailable due to error: %@", error);
[self disableRemoteNotificationFeatures];
}
推送器从PKCS12文件中读取证书和密钥数据。这是一个包含X.509证书和私钥的单一文件的二进制格式。OpenSSL CLI提供了将其他文件格式转换到PKCS12和从PKCS12转换到其他文件格式的功能。
检查PKCS12
openssl pkcs12 -in pusher.p12
输出的内容可能是
...
friendlyName: Apple Development/Production IOS/Mac Push Services: <your-bundle-identifier>
localKeyID: <key-id>
...
-----BEGIN CERTIFICATE-----
...
friendlyName: <private-key-used-for-generating-above-certificate>
localKeyID: <same-key-id>
...
-----BEGIN PRIVATE KEY-----
...
确保您的构建与开发/生产
、iOS/Mac
和bundle标识符匹配。
检查PKCS12结构
openssl pkcs12 -in pusher.p12 -info -noout
检查PEM
openssl rsa -in pusher.pem -noout -check
openssl rsa -in pusher.pem -pubout
openssl x509 -in pusher.pem -noout -pubkey
PKCS12转换为PEM
openssl pkcs12 -in pusher.p12 -out pusher.pem -clcerts -aes256
或者您可以使用以下命令,该命令不会加密私钥(不推荐)
openssl pkcs12 -in pusher.p12 -out pusher.pem -nodes -clcerts
PEM转换为PKCS12
openssl pkcs12 -export -in pusher.pem -out pusher.p12
更多详细信息,请查阅OpenSSL文档:OpenSSL文档 - pkcs12
苹果的推送通知服务在本质上是宽容度不高的。如果顺序不当或数据格式错误,该服务将拒绝交付任何通知,但通常很少提供错误信息和修复方法。在最坏的情况下,它甚至不会通知客户端就断开连接。
以下是一些注意事项
设备token对于设备、开发者的证书以及app是用生产还是开发(沙盒)证书构建的独特标识。因此,请确保推送证书与app的配置文件完全匹配。这并不意味着token总是不同;对于不同的bundle标识符,设备token可能是相同的。
苹果响应推送通知有两个渠道:通知连接和反馈连接。它们都异步操作,例如,在发送第二个推送之后,我们可能会收到对第一个推送的响应,说明其有效载荷无效。为每个通知使用新的标识符,以便可以将这些响应链接到正确的通知。
如果连接失败,则检查
证书和密钥是否有序?使用上面的OpenSSL命令检查证书。看看是否有一个推送证书和密钥存在。同时确保你已经在线,尝试 ping www.apple.com
。
证书是否正确加载?尝试使用 [NWSecTools identityWithPKCS12Data:data password:password error:&error]
或 [NWSecTools keychainIdentityWithCertificate:certificate error:&error]
初始化一个身份。
你使用的是正确的身份吗?使用 [NWSecTools inspectIdentity:identity]
检查身份实例。一般来说,NWSecTools
可以帮助检查证书、身份和钥匙串。
你能连接到推送服务器吗?尝试使用 [NWPusher connectWithIdentity:identity error:&error]
或 [NWPusher connectWithPKCS12Data:pkcs12 password:password error:&error]
。
推送者与主机 gateway.push.apple.com
和 gateway.sandbox.push.apple.com
在端口 2195
接通,与主机 feedback.push.apple.com
和 feedback.sandbox.push.apple.com
在端口 2196
接通。确保你的防火墙配置允许这些连接。
如果没有内容发送到设备,则检查
设备是否在线?它能从其他服务接收推送通知吗?尝试从其他应用获取推送通知,例如一个消息传递应用。许多无线连接看起来工作得很正常,但不发送推送通知。尝试切换到其他Wi-Fi或蜂窝网络。
你是否向正确的设备令牌推送?此令牌应由接收设备的操作系统在回调 -application: didRegisterForRemoteNotificationsWithDeviceToken:
中返回。推送证书应与应用程序的配置文件匹配,检查 开发或生产、iOS或Mac 及 捆绑标识符,确保接收的应用程序已关闭,以免干扰交付。
推送调用成功了吗?是否有来自推送服务器或反馈服务器的不良响应?[pusher pushPayload:payload token:token identifier:rand() error:&error]
和 [pusher readFailedIdentifier:&identifier apnError:&apnError error:&error]
都应返回 YES
,但推送和读取之间存在延迟。此外,尝试连接到反馈服务以读取反馈。
有关更多故障排除技巧,请参考苹果的文档:推送通知故障排除
源代码附带的Xcode项目文件应负责构建OS X和iOS演示应用程序。或者你还可以使用 xcodebuild
从命令行构建 Pusher.app
。
xcodebuild -project NWPusher.xcodeproj -target PusherMac -configuration Release clean install
构建成功后,可以在项目的 build
文件夹中找到 Pusher.app
。
使用 appledoc 从项目根目录运行生成的文档并安装。
appledoc .
有关更多信息,请参阅 appledoc 文档。
Pusher 根据BSD 2-Clause 许可证许可,请参阅包含的 LICENSE 文件。