Fork me on GitHub

使用 Vapor3 进行 APNS 推送

本文不完全翻译自 Medium(由于完全按照原文教程并不能正常使用,本人采坑后总结了不少经验,希望能帮助大家少走弯路,一次性成功)。

img

从 Swift 内部执行 cURL 命令

几周前,我决定学习 Vapor3 框架。我希望能够为未来的项目开发一个后端API,而不需要依赖我的 goto, Parse Server。这是一个快速操作指南,告诉你 Vapor 完全可以胜任!本文涉及到的所有代码都可以在我的 GitHub 里找到。

为了获得对框架的一些经验,并查看一些用例示例,我从 Ray Wenderlich 的网站上购买了 “Server Side Swift with Vapor” 电子书。我把书从头到尾读了一遍以了解基本知识。下一步准备尝试将我常用的解析服务器的功能移植到一个 Vapor3 应用程序上。不过当我要实现推送通知时,我遇到了一个障碍。

苹果的 APNs 需要使用 HTTP/2 进行连接,但是 SwiftNIO (Vapor3的核心网络依赖)还不支持HTTP/2协议。我想我可能需要使用一些第三方的解决方案,但我不想要那种依赖。我决定浏览一下苹果的开发文档,看看使用cURL连接APNs需要做些什么。

首先,您需要一个付费的 Apple Developer 帐户和一个您已经配置了推送通知的演示应用程序。您将需要运行演示应用程序并获得设备令牌,该令牌将稍后发送到测试APNs。

HTTP/2 cURL 请求需要一个证书。第一步是从 Apple developer 帐户生成所需的证书。我不会详细介绍如何做到这一点,因为已经有几个教程了。需要合并 .cer 文件和 .p12 文件。要做到这一点,请遵循以下 bash 脚本:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash

# Convert the .cer file into a .pem file:
openssl x509 -in aps_development.cer -inform der -out cert.pem

# Convert the private key’s .p12 file into a .pem file:
openssl pkcs12 -nocerts -in aps_development.p12 -out key.pem

# Finally, combine the certificate and key into a single .pem file
cat cert.pem key.pem > aps_development.pem

 

有了新的公共/私有组合证书,我们现在可以尝试使用 cURL 与 APNs 通信。

(译者注)这里有个注意点,一般情况下我们使用的 curl 工具是系统自带的,版本可能较低

如果出现了 curl: (1) Unsupported protocol http2 错误,则需要更新curl或者安装带有nghttp2的版本,这里墙裂推荐使用Homebrew进行安装,一条命令搞定:brew install curl --with-nghttp2,如果已经安装过了,就重装一下:brew reinstall curl --with-nghttp2,安装好了之后不要忘记 根据提示 添加并刷新环境变量。

1
curl -d <apns_payload> -H "apns-topic:<bundleId>" -H "apns-expiration:1" -H "apns-priority:10" --http2-prior-knowledge --cert /Users/bruce/Desktop/Demo/Hello/private/aps_development.pem:<password> https://api.development.push.apple.com/3/device/<device_token>

请求成功了(没有报错即代表成功)!现在我们能够在App上收到通知。最后一步是把代码封装一下,便于将其接入到 Vapor3 应用程序中,在后台启动cURL请求。你可以在这里找到注册 Shell 服务的代码:

https://github.com/nathantannar4/the.phoenix.project/blob/master/Sources/App/Services/Shell.swift

该服务可以加入我们所需的参数并执行 bash 命令。下面是 Vapor3 的简单示例:

(译者注)为了避免大家采坑,这里给出一个完整的示例,请一定要按照格式来填充内容,否则Shell很可能执行出错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let shell = try req.make(Shell.self)
let jsonAPNSPayloadString = "{\"aps\":{\"alert\":{\"body\":\"您有一条新消息\"},\"badge\":1,\"sound\":\"default\"},\"f\":\"6001\",\"t\":\"6006\",\"m\":\"373360335316321408\"}"
let bundleId = "com.qianbao.merchantApp"
let certPath = "/Users/bruce/Desktop/Demo/Hello/private/aps_development.pem"
let password = "123456"
let apnsURL = "https://api.development.push.apple.com/3/device/"
let token = "a452171d01a3df308449a63a730d616190a1cd587bda4cd8086c2651a32ba383"

let arguments = ["-d", jsonAPNSPayloadString, "-H", "\"apns-topic:\(bundleId)\"", "-H", "\"apns-expiration:1\"", "-H", "\"apns-priority:10\"", "--http2-prior-knowledge", "-E", "\(certPath):\(password)", apnsURL + token]
print(arguments.joined(separator: " "))

let res = try shell.execute(commandName: "/usr/local/opt/curl/bin/curl", arguments: arguments)
let _ = res.map { (data: Data) in
let s = String.init(data: data, encoding: .utf8) ?? "null";
print(s)
}

(译者注)不要忘记在 configure.swift 中添加服务

1
services.register(Shell.self)

这只是一个简单的临时方案。很快,SwiftNIO 将支持 HTTP/2,届时,Vapor3 也会支持 HTTP/2。在社区中已经存在一个很好的 repo (https://github.com/vapor-community/apns)。到时候在 Vapor3 应用中进行 APNs 请求的编码/解码将变得更加简单。

iOS端只需要创建个壳工程,把推送的代码集成进去,启动一次,允许发送通知,然后拿到 APNS 的 DeviceToken 就可以愉快的进行推送测试啦,have a enjoy day 😄。

------------- 本文结束感谢您的阅读 -------------