NWPusher 0.7.5

NWPusher 0.7.5

测试已测试
语言语言 Obj-CObjective C
许可证 BSD
发布上次发布2017年4月

维护者 Leo Vandriel



NWPusher 0.7.5

  • Leonard van Driel

Pusher

基于 OS X 和 iOS 的应用程序和框架,用于与苹果推送通知服务 (APNs) 交互

Pusher OS X

安装

使用 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 服务发送推送通知

  • 直接从 密钥链 获取 证书和密钥
  • 完全可定制的 有效载荷 带有 语法检查
  • 允许设置 过期时间优先级
  • 保存 设备令牌,无需每次都复制粘贴
  • 处理 PKCS #12 文件 (.p12)
  • 沙盒 自动配置
  • 报告 APNs 返回的 详细错误消息
  • 反馈服务 读取

OS X 和 iOS 框架,用于从您的应用程序发送推送

  • 模块化、无依赖,使用您喜欢的
  • 全面文档化的源代码
  • 详细的错误处理
  • 与 iOS 兼容,因此您也可以从您的 iPhone 直接推送 :o
  • 针对两个平台的应用演示

入门指南

在开始发送推送通知数据包之前,有一些难题需要克服。首先,您需要获取要发送通知的应用的Apple 推送服务 SSL 证书。此证书由 Pusher 用于设置 SSL 连接,通过该连接将发送数据包到 Apple。

其次,您还需要获取要发送数据包的设备的设备令牌。每个设备都有其唯一标识符,只能从应用内部获得。这有些复杂,但最终只需要在 Apple 的开发者中心网站上点击几下,耐心等待即可。

证书

让我们从 SSL 证书开始。目标是获取证书和私钥并放入您的 OS X 密钥链中。如果其他人已经生成了此证书,您需要请求将这些证书导出为 PKCS12 文件。如果没有生成证书,您可以在以下步骤中生成证书和私钥。

  1. 登录到Apple 的开发者中心
  2. 转到 配置文件门户证书、标识符和配置文件
  3. 转到 证书 并创建 Apple 推送通知服务 SSL
  4. 接下来,您将进入证书生成过程。

请注意,您将最终下载一个证书,需要将其与私钥一起安装到密钥链中。它看起来应该像这样

Keychain export

注意:有 开发生产 证书,它们应该(通常)分别对应于您的应用的 DEBUGRELEASE 版本。请确保您获取了正确的证书,检查 开发(沙盒)或生产iOS 或 Mac,以及 捆绑标识符

推送证书应导出为 PKCS12 文件,这允许您与同行开发者共享这些文件。

PKCS12 file

设备令牌

现在您需要获取一个设备令牌,这是一个 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 位推送字符串将被记录到控制台。

从 OS X 推送

在将 SSL 证书和私钥放入密钥链中,并拥有设备令牌后,您就可以发送推送通知了。让我们先通过 Mac OS X Pusher 应用 发送通知。打开 Pusher Xcode 项目并运行 PusherMac 目标。

Pusher OS X

顶部组合框列出了密钥链中可用的 SSL 证书。选择您想要使用的证书,粘贴要推送设备的设备令牌。下面的文本框显示了您要发送的 JSON 格式的数据包文本。有关此格式的更多信息,请参阅 Apple 文档中的 Apple 推送通知服务

在按 推送 之前,确保您要 推送到的 应用处于 后台,例如通过按主页按钮。这样,您就可以确保应用不会干扰消息。按推送,等待几秒钟,您应该会看到通知进入。

如果一切正常的话,您可以查看下面的 故障排除 部分。

Pusher OS X

从 iOS 推送

最好的体验当然是直接从一部iPhone推送到另一部iPhone。这可以通过Pusher iOS应用程序来完成。在运行PusherTouch目标之前,请确保在应用程序中包含了证书、私钥和设备令牌。将您之前导出的PKCS12文件包含到PusherTouch捆绑包中。然后转到文件夹中的NWAppDelegate.m,配置pkcs12FileNamepkcs12PassworddeviceToken。现在运行PusherTouch目标。

Pusher iOS

如果一切设置正确,您只需连接推送。然后您应该会在设备上收到测试..推送消息。

再次,如果事情并不像预期的那样运作,请查看下方的故障排除部分或提交一个在GitHub上。

有关APNs架构的更多信息,请参阅Apple的文档: Apple推送通知服务

从代码推送

Pusher还可以作为一个框架来程序化地发送通知。包含的Xcode项目为OS X和iOS提供了示例。通过CocoaPods包含NWPusher是迄今为止最简单的方法。

pod 'NWPusher', '~> 0.7.0'

CocoaPods还会编译文档,这些文档可以通过CocoaDocs访问。或者,您也可以从Classes文件夹中仅包含所需的文件。确保您与Foundation.frameworkSecurity.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

在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.comgateway.sandbox.push.apple.com 在端口 2195 接通,与主机 feedback.push.apple.comfeedback.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构建

源代码附带的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 文件。

作者