本文由 简悦SimpRead 转码,原文地址 medium.com
用一个位于GitHub上的bash脚本从命令行发送iOS推送通知,这里......
使用位于GitHub上的单个bash脚本从命令行发送iOS推送通知 here 不使用外部依赖或服务。
Photo by Osman Rana on Unsplash
我已经在我建立的大多数应用程序中使用推送通知有一段时间了。在iOS上,它们是远程通知功能的核心。它们快速、安全,并且减少了开发者为了向他们的应用和用户有效传播信息而需要做出的努力。
此外,现在你可以在其中显示丰富而全面的内容。我已经写了一篇关于这个问题的文章这里,你可能会喜欢阅读,同时发现它对你自己的应用开发很有用。
因此,虽然我对这项技术很满意,但有一个领域我一直发现自己想要的,那就是测试它们;我的意思是能够将它们发送到一个特定的设备上,该设备上安装有我的应用程序,并持续重复许多次,以便我可以测试、调整和重写其文本和有效载荷。
测试iOS上的推送通知
通常情况下,你可能有自己的服务器设置,或者你使用已经存在的许多推送通知服务中的一个。所有这些都需要大量的时间和精力来启动和运行。理想情况下,你可能希望只用一台Mac、一台iOS设备和零钱(当然不包括你买Mac和设备的钱)就开始发送通知。
如果你在网上做了一些研究,你可能已经遇到了NWPusher,它工作得很好,但只适用于基于证书的认证,它直接从钥匙链上读取证书和钥匙。它还作为一个Mac应用程序分发,你需要安装,所以它只在该平台上工作。你还必须每年自己生成这些证书,而且每次都是一个多步骤的过程。
shell脚本解决方案
我没有使用基于证书的通知,而是使用基于令牌的通知,这更容易设置和管理。另外,我没有创建一个图形化的应用程序来发送通知,而是写了一个bash shell脚本来做这件事。它被称为 "pu.sh",位于GitHub这里,在那里也有完整的文档。同时,你也可以跟着我在下面看更全面的演练。
我还想指出的是,用shell脚本的解决方案可以保证透明度。你可以事先知道脚本对你提供的信息做了什么,也就是它只向APNs服务器发送了一个有效载荷。
下面两节是官方文档这里中的信息的浓缩版。对于我们的解决方案,我们将使用基于令牌的推送通知,而不是基于证书的通知。
基于令牌的APN连接
基于令牌的认证比基于证书的通信更快,因为它不需要APN去查找与你的服务器有关的证书。你可以对多个供应商服务器使用相同的令牌,你的所有公司应用程序可以使用一个令牌。请记住,你必须使用你的签名密钥每小时至少更新一次你的令牌。我们将在短期内了解如何更新你的令牌。代币的有效期也是随你喜欢,直到你撤销它们,而证书每年都必须重新发行和重新分配。
获得一个加密密钥和一个密钥ID
你需要一个APNs认证令牌签署密钥来生成你的服务器所使用的令牌,以便发送推送通知。你可以在developer.apple.com的 密钥 ➙ 所有 部分的开发者账户中申请这个密钥,如下所示。
继续到下一步后,你会得到。
- 一个包含钥匙ID的10个字符的字符串。把这个放在手边,你会需要它。如果你忘记了,它仍然可以在开发者门户网站上找到。
- 一个作为
.p8文本文件的签名密钥。把它放在安全的地方。例如,不要把它放在你的源代码库里。如果你丢失了它,它就永远消失了,你将不得不撤销它并重新生成它。如果这个密钥以任何方式被破坏,它可以被用来向你的应用程序发送推送通知🙀,所以如果你怀疑发生了这种情况,请撤销它并申请一个新的密钥。
你已经可以看到这个过程比创建密钥、证书等要简单得多。
获得一个设备令牌
下面是你的应用程序应用委托的方法,以及一些简短的解释评论,你需要获得一个设备令牌。
/*
* Here you register for remote notifications
*/
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
application.registerForRemoteNotifications()
return true
}
/*
* Here you print out your device token
*/
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.hexEncodedString()
// now the token will appear in the xcode console
print(token)
}
/*
* This helper method converts the device token frm Data to String
* so you could just paste it into the script below
*/
func hexEncodedString() -> String {
return map { String(format: "%02hhx", $0) }.joined()
}
你可能已经注意到,在上面的代码中,我们并没有询问用户的权限。在这篇文章中,我们将通过在通知有效载荷中设置content-available键为1值来使用无声通知。不过对于你自己的实现来说,你当然可以省略这个标志,并询问用户是否允许向他们发送推送通知。现在你可以自由地发送你想要的任何类型的有效载荷。这里有很多信息可以帮助你。
让我们创建并加密我们的令牌
安装OpenSSL
如果你已经安装了OpenSSL,你可以跳到下一节。
为了生成我们的令牌,我们需要在我们的机器上安装OpenSSL。如果你使用的是mac(如果你正在构建iOS应用程序,你可能是),请执行以下步骤。
- 打开一个终端
- 安装homebrew。
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"3. - 输入
brew install openssl。
完成了!
生成一个令牌
下面是生成令牌的bash脚本。你必须提供你的团队ID(在你的开发者账户中找到)、密钥ID(在我们前面创建密钥的章节中找到)和你在上一步中创建并保存在安全地方的p8文件。
#!/usr/bin/env bash
# your team id located in the developer portal
TEAMID=.....
# your key id located in the developer portal
KEYID=......
# the path to the key file you have stored in a safe place
SECRET="<the path to the file you have kept in a safe location>.p8"
# make input base64 url safe
function base64URLSafe {
openssl base64 -e -A | tr - '+/' '-_' | tr -d =
}
# sign input with you key file
function sign {
printf "$1"| openssl dgst -binary -sha256 -sign "$SECRET" | base64URLSafe
}
# now
time=$(date +%s)
# your header section
#
# e.g.
# {
# "alg" : "ES256",
# "kid" : "ABC123DEFG"
# }
header=$(printf '{ "alg": "ES256", "kid": "%s" }' "$KEYID" | base64URLSafe)
# your claims section
#
# e.g.
# {
# "iss": "DEF123GHIJ",
# "iat": 1437179036
# }
claims=$(printf '{ "iss": "%s", "iat": %d }' "$TEAMID" "$time" | base64URLSafe)
# concatenate your header, your claim and a signed version of you header concatenated with your claim
jwt="$header.$claims.$(sign $header.$claims)"
# show it
echo $jwt
要执行该脚本,请将其放在一个".sh "文件中(例如:script.sh),并在该脚本所在目录的终端中输入"./script.sh"。
你的输出将类似于以下内容(三个字符串由两个点连接)。
eyAiYWxnFCIiB9.eyAiaXNz1OTMwOCB9.MEUCIQDRXq8MwIQP5zeSnpwlQ。
这是一个JSON网络令牌,简称JWT。你可以在jwt.io/ 找到更多关于它们的信息,同时还有大量的许多语言的签名和验证库。
向一个设备发送推送通知
现在我们有了发送推送通知所需的一切,那就是一个签名的令牌和一个设备标识符。在下面的脚本中,你只需要提供你的应用程序的捆绑ID和你之前从Xcode控制台获得的设备标志。
#!/bin/bash
# Development server: api.sandbox.push.apple.com:443
#
# Production server: api.push.apple.com:443
ENDPOINT=https://api.sandbox.push.apple.com:443
URLPATH=/3/device/
BUNDLEID=...
DEVICETOKEN=...
URL=$ENDPOINT$URLPATH$DEVICETOKEN
JWT=<token created by the previous script>
curl -v \
--http2 \
--header "authorization: bearer $JWT" \
--header "apns-topic: ${BUNDLEID}" \
--data '{"aps":{"content-available" : 1, "data":"Some Data"}}' \
"${URL}"
注意:开发与生产
将上面的 "ENDPOINT "文本替换为脚本顶部的注释部分中的相应URL。当直接从Xcode运行您的应用程序到设备上时,使用开发,而在其他情况下,即Testflight、Adhoc、Enterprise或Appstore,使用生产。
接收推送通知
如果你有一个物理设备(不是模拟器,推送通知在模拟器中不工作)插入或无线连接到Xcode,运行这个脚本将发送一个通知到该设备。你可以在下面验证这一点。
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable : Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
print(userInfo)
}
你刚刚收到了一个推送通知 🚀
注意:丰富的推送通知
为了发送丰富的推送通知,导入UserNotifications框架,并在AppDelegate.swift中添加UNUserNotificationCenterDelegate。然后在didFinishLaunchingWithOptions某处添加这个。
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options:[.badge, .alert, .sound]) { (granted, error) in
}
你可以让上面的脚本中的--data包含这个有效载荷(作为单行的字符串)。
{
"aps": {
"alert": {
"body": "Push notification body",
"title": "Push notification title"
},
"mutable-content": 1,
category: "rich-apns"
},
"media-url": "https://i.imgur.com/t4WGJQx.jpg"
}
如果你想知道更多关于如何编写和发送丰富的推送通知,你可以阅读我的文章这里。
不幸的是,当出现问题时,从APNS返回的反馈有很多不足之处。这些是可能被返回的状态代码,在前面脚本的输出中显示。
200: 成功
400: 错误的请求
403: 证书或提供者认证令牌有错误
405:请求使用了一个错误的:方法值。只支持POST请求。
410:设备令牌对该主题不再有效。
413:通知的有效载荷太大。
429:服务器收到太多对同一设备令牌的请求。
500:内部服务器错误
503:服务器正在关闭,无法使用。
如果状态代码是200,一切都很好,你收到了推送通知。
一般来说,你应该总是使用最近的设备令牌,设备令牌实际上应该来自你试图发送的设备,你应该确保团队ID、密钥ID和捆绑ID的拼写正确。
对于一个成功的请求,响应的主体是空的。失败时,响应主体包含一个JSON字典,其中的 "reason "键可以让你大致了解出错的原因,例如 "BadDeviceToken"。
分开的想法
我希望你发现这个解决方案对你的推送通知测试有帮助。我也希望你发现这里的信息对你自己的应用开发有足够的价值。
谢谢你 ❤️