Swift Server Push Notification 配置

Swift Server Push Notification 配置
Photo by Jakub Żerdzicki / Unsplash

获取证书

在 Apple Developer 开发者账号 Certificates, Identifiers & Profiles 里选择 Keys。新增一个 key, configure 里选择 Sandbox & Production。下载该 p8 证书,并且保存好(只能下载一次)。

终端 cd 到证书所在路径,输入下面指令。

openssl pkcs8 -nocrypt -in AuthKey_XXXXXXXXX.p8 -out ~/Downloads/key.pem

cat key.pem

得到 PRIVATE KEY 字符串,复制好。

服务端配置

服务端有多种技术栈方案,包括 Java、NodeJS、Swift、Python 等。这里采用 Swift Server 方案。

Swift Server 整体使用 Vapor 框架,具体实现可以参考官方文档。对于 Apple Push Notification 的实现使用 APNSwift 模块。

APNSwift 初始化需要配置推送证书等信息。

static let deviceToken = ""
static let appBundleID = ""
static let privateKey = """
"""
static let keyIdentifier = ""
static let teamIdentifier = ""
    
let client = APNSClient(
    configuration: .init(
        authenticationMethod: .jwt(
            privateKey: try .init(pemRepresentation: privateKey),
            keyIdentifier: keyIdentifier,
            teamIdentifier: teamIdentifier
        ),
        environment: .development
    ),
    eventLoopGroupProvider: .createNew,
    responseDecoder: JSONDecoder(),
    requestEncoder: JSONEncoder()
)

// Shutdown the client when done
try await client.shutdown()
  • privateKey 就是上面 cat key.pem 得到的 PRIVATE KEY 字符串,包含 -----BEGIN PRIVATE KEY----------END PRIVATE KEY-----
  • teamIdentifier 是 Apple Developer 的团队 id。
  • keyIdentifier 是上面在 Apple Developer 生成推送 p8 证书时,Key 对应的 KEY ID
  • deviceToken 是移动端 iOS app 生成的 push device token
Key ID

客户端配置

iOS 项目需要设置 Push Notification 的 Capability。

Push Notifications Capability

然后在 app 启动后获取通知权限,这样才能拿到 push device token。

import UIKit

final class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // 请求推送通知权限
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
            if granted {
                // 注册 APNs
                DispatchQueue.main.async {
                    application.registerForRemoteNotifications()
                }
            } else {
                print("User denied push notifications")
            }
        }
        return true
    }

    // 处理推送通知注册成功后的设备 token
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        print("Device Token: \(deviceToken)")
        let deviceTokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
        print("Device Token String: \(deviceTokenString)")
        // 将 deviceToken 发送到你的服务器
    }

    // 处理推送通知注册失败
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Failed to register for remote notifications: \(error)")
    }
}

拿到 deviceToken 后,将其发送给服务器存储。服务器发送推送时,需要指定 deviceToken 才能知道是对哪一台手机发送推送。

尝试发送简单推送

Server 调用下面方法,传入初始化的 APNSClient 实例。不出意外的话,手机就能收到推送了。

func sendSimpleAlert(with client: some APNSClientProtocol) async throws {
    try await client.sendAlertNotification(
        .init(
            alert: .init(
                title: .raw("Simple Alert"),
                subtitle: .raw("Subtitle"),
                body: .raw("Body"),
                launchImage: nil
            ),
            expiration: .immediately,
            priority: .immediately,
            topic: self.appBundleID,
            payload: EmptyPayload()
        ),
        deviceToken: self.deviceToken
    )
}

Read more

《漫步华尔街(第12版)》读书笔记

《漫步华尔街(第12版)》读书笔记

股票分析 基本面分析 * 基本面分析的四个基本决定因素 * 预期增长率 * 复合增长(复利)对投资决策有很重要的意义。 * 一只股票的股利增长和盈利增长率越高,理性投资者应愿意为其支付越高的价格。 * 推论:一只股票的超常增长率持续时间越长,理性投资者应愿意为其支付越高的价格。 * 预期股利支付率 * 对于预期增长率相同的两只股票来说,持有股利支付率越高的股票,较之股利支付率低的股票,会使你的财务状况更好。 * 在其他条件相同的情况下,一家公司发放的现金股利占其盈利的比例越高,理性投资者应愿意为其股票支付越高的价格。 * 特例,很多处于强劲增长阶段的公司,往往不支付任何股利。这时候不满足「在其他条件相同的情况下」。 * 风险程度 * 在其他条件相同的情况下,一家公司的股票风险越低,理性投资者(以及厌恶风险的投资者)应愿意为其股票支付越高的价格。 * 市场利率水平 * 在其他条件相同的情况下,市场利率越低,理性投资者应愿意为股票支付越高的价格。 * 举例,银行存款利率

By Gray
2025 端午日本九日游

2025 端午日本九日游

从日本回来后就一直忙个不停,忙着搬家和工作。这周末终于有时间回顾和记录一下日本的旅游行程。 这次出国游是年初就规划好的。端午节假期三天再加上节后请假四天,以及周末,总共能休 9 天。5 月 31 号出发,6 月 9 号凌晨的航班飞回北京。 出发前的准备 机票和酒店 越临近出发日期,机票和酒店就越贵。所以我们早早地就把机票和酒店定了。 去程机票订的山航,青岛转机,5 月 31 号从北京出发抵达青岛,在青岛玩一天,翌日早上从青岛飞往关西机场。回程机票订的海南航空,从东京羽田机场直飞北京,是凌晨两三点的红眼航班。 本次行程要去关西(京都、大阪、奈良)、关东(东京、富士山)。关西三个城市很近,一直住在京都即可,从京都往返大阪和奈良。关东就住在东京。京都的酒店订在了京都站附近,出站走几步就能到,交通非常便利。东京的酒店订在了马喰町附近,附近有很多地铁线路,包括浅草线、

By Gray
2025 关税危机中学到的投资经验

2025 关税危机中学到的投资经验

充足的现金流很重要 好的买入机会不会每天都出现,但当它出现的时候,你最好还有筹码可以投入。 有些人手里握不住钱,一有闲钱就赶紧买入基金、股票,生怕错过了机会,让钱白搭手里。市场是疯狂的、充满变数的,尤其是在特朗普上台后,一句话就可能让股市涨停或跌停。那些专业的理财投资机构尚不能预测市场,何况我们这些散户呢。在不稳定的市场中,我们要学习巴菲特,备好现金,耐心等待买入(抄底)机会。 不要提前打光子弹 美股标普 500 指数从 2 月中旬到 3 月中旬累计跌了约 10%。如果这时候你觉得已经跌了很多,可以 all in 抄底了,那么你就会错过 4 月上旬的那次狂跌——一周跌了约 10%。没有人能预测市场,除了此刻的股市指挥家特朗普。散户们能学到的经验就是「永远不要提前打光子弹」,你以为的谷底其实只是个半山腰。 相信自己,保持耐心 在美股大跌的时段里,小红书、v2ex

By Gray