“钱包”的新功能
What’s new in Wallet
2026年6月10日
一句话判断
✅ Apple 终于把 Wallet Pass 的生产工具链现代化了,彻底告别手搓 pass.json 和第三方签名库的时代。
这场 Session 讲了什么
以前做个 Apple 钱包通行证(Wallet Pass),你得在文本编辑器里盲写 pass.json,靠脑补 UI,然后找一堆第三方库来处理证书签名和打包。现在 Apple 给了官方解法:Mac 上出了个所见即所得的 Pass Designer 让你拖拽设计,服务器端直接扔给你一个 Swift on Server 的 Pass Builder 包来批量生成和签名。
除了工具链大换血,iOS 27 还加了个适合放高清大图的 Poster Generic 通行证样式,以及能在通行证下方直接挂两个快捷操作(Featured Actions)的 API。这对做会员系统、票务和零售后端的开发者影响最大,以后发 Pass 就像发推送一样丝滑,不用再跟玄学的 JSON 格式和过期的 Python 签名脚本死磕了。
值得深挖的点
Pass Builder 让 Swift on Server 有了真正的杀手级场景
Apple 推 Swift on Server 喊了好几年,但一直缺个“非它不可”的场景。Pass Builder 就是那个破局点。以前后端生成 Pass,要么用年久失修的开源库,要么自己用 OpenSSL 手撸 PKCS#7 签名。现在 Apple 官方下场,把 PassPackage 和签名逻辑封装成了原生的 Swift 包。
这个设计的聪明之处在于它没有强迫所有后端必须用 Swift。如果你是用 Java,他们提供了 swift-java 绑定;如果你用 Go 或 Rust,他们直接开放了 Pass Package 格式的 protobuf 定义,并提供了 buildpass 命令行工具。这意味着你可以用任何语言生成类型安全的模型,最后调一下命令行完成签名。Apple 在“推广 Swift”和“适应企业现有后端架构”之间找了个非常务实的平衡。
Poster Generic 与 UI 碎片化的妥协
新增的 Poster Generic 样式明显是为了抢佔线下零售和会员卡的视觉高地,允许用大背景图做视觉锤。但这也带来了严重的碎片化问题。Apple 在 API 设计上做了一些硬性限制,比如 Poster Generic 的底部字段(Footer fields)只允许显示一个,多写直接忽略。
更坑的是向下兼容。为了让 iOS 26 及以前的老设备不白屏,你必须在 pass.json 里同时写 posterGeneric 和 generic 两套字段。这就像当年适配 iPhone X 的刘海屏一样,Apple 把 UI 适配的脏活累活又扔回给了开发者。你在享受新样式带来的高转化率时,必须忍受维护两套 JSON 结构的痛苦。
代码片段
1. 使用 Pass Builder 在服务器端生成 Pass
场景:在 Swift on Server 中读取模板,填入用户数据并签名。
import PassBuilder
// 根据用户数据生成个性化的通行证
func createPass(for user: MemberModel) async throws -> URL {
// 加载 Pass Designer 导出的模板
var package = PassPackage(url: "template.pkpasstemplate")
// 填充动态字段,类型安全,告别手拼 JSON
package.pass.fields.setValue(user.name, forKey: "USER_NAME")
package.pass.fields.setValue(user.level, forKey: "MEMBER_LEVEL")
// 替换背景图为用户头像
package.background = PassImage(url: user.avatarURL)
// 设置条形码
package.pass.barcodes = [
Pass.Barcode(message: user.memberId, format: .pdf417)
]
// 打包并签名,返回 .pkpass 文件路径
return try await package.build()
}
坑:PassPackage 是值类型(Struct),修改属性时记得用 var 声明,且 build() 是异步操作,别在同步路由里死锁。
2. 配置 Poster Generic 并兼容旧版 iOS
场景:在 pass.json 中启用新样式,同时保证 iOS 26 用户能正常打开。
{
"formatVersion": 1,
"passTypeIdentifier": "pass.com.example.membership",
"posterGeneric": {
"headerFields": [{ "key": "memberID", "label": "No.", "value": "102035" }],
"footerFields": [{ "key": "type", "value": "VIP" }]
},
"generic": {
"headerFields": [{ "key": "memberID", "label": "No.", "value": "102035" }],
"primaryFields": [{ "key": "type", "label": "Type", "value": "VIP" }]
}
}
坑:如果你在 posterGeneric 的 footerFields 里塞了多个字段,iOS 17 只会默默显示第一个,不会报错,排查起来极其恶心。
3. 添加 Featured Actions (快捷操作)
场景:在通行证下方显示“查看积分”和“使用优惠券”按钮。
{
"featuredActions": [
{
"identifier": "view-points",
"type": "loyaltyPoints",
"url": "https://example.com/points"
},
{
"identifier": "use-coupon",
"type": "membershipBenefits",
"url": "https://example.com/coupons"
}
]
}
坑:最多只能配 2 个 Action,且必须按优先级排序。URL 必须是 HTTPS,且最好在 App 里配置好 Universal Links 以实现无缝跳转。
最佳实践
已有项目如果还在用 Python/Node.js 的第三方库生成 Pass,建议立刻把签名和打包逻辑迁移到官方的 buildpass 命令行工具或 Pass Builder 库。这能彻底解决证书过期、OpenSSL 版本冲突等运维噩梦。
新项目强烈建议采用“设计-构建”分离模式。让设计师或产品经理在 Mac 上用 Pass Designer 调整 UI 并导出 .pkpasstemplate,后端只负责拉取模板、注入数据并调用 build()。把 UI 调整从后端代码里剥离出去,能省去 80% 的跨部门沟通成本。
处理新条形码(如 EAN-13, Code 39)时,永远在 barcodes 数组里垫底提供一个 QR Code 或 PDF417 作为 fallback。不要假设所有线下门店的扫码枪都升级了固件,扫不出码让顾客卡在闸机口是严重的生产事故。
还有什么值得关注
- Protobuf 定义开放:Apple 公开了 Pass Package 的 protobuf 定义,Go/Rust 等后端语言可以生成强类型的 Pass 模型,不用再手写 JSON 序列化。
- Pass Designer 的 WYSIWYG 体验:终于不用改一行 JSON 刷新一次模拟器了,Mac 上的实时渲染和真机几乎一致,支持直接拖拽图片。
- 语义化 Action 类型:Featured Actions 提供了如
loyaltyPoints等预设类型,Wallet 会自动匹配对应的 SF Symbols 图标和多语言文案,省去了自己找图标的麻烦。