Highlight
Wallet 在 2025 引入
upcomingPassInformation,让一张通行证能装下整个巡演或赛季的多个场次,每个场次有独立的identifier、isActive状态、场馆语义和详情页。
核心内容
票务 App 的老问题是:一个赛季 20 场球赛,用户得在 Wallet 里堆 20 张票。月历视图里全是同一支球队的 logo,找哪一场全靠生日。去年 Wallet 推出的 Poster Event Ticket 改了视觉,但底层数据模型没动——每场仍是独立 pass。今年的 upcomingPassInformation 才是真正动了数据结构:一张 pass 里携带一个事件数组,每个事件有自己的 ID、日期、场馆、艺人、座位号和独立的事件指南。用户进入这张 pass 后看到的是一个”行程列表”,点进去才是单场详情。开发者迁移成本主要在两点:稳定的 identifier 用于增删事件,isActive 字段用于标记当前可入场的场次。
登机牌那边的改动更激进。Apple 直接把航班状态追踪接进了系统级的 Apple Flight Service:加票时 Wallet 用 airlineCode、flightNumber、originalDepartureDate 三个语义字段去查找航班,之后登机口变更、延误、航站楼调整都由系统推送,不再依赖开发者后端的 APNs 轮询。登机时间也不需要单独维护——Wallet 在加票瞬间记下”起飞时间 - 登机时间”的差值,起飞时间被官方系统更新时,登机时间按这个差值自动平移。配合新的 Live Activity,用户在锁屏和灵动岛就能盯到落地,还能把这个 Live Activity 通过 Messages 分享给接机的人。
还有一个容易被忽略的改动:自动添加通行证。PKPassLibrary 新增了一次性授权能力 backgroundAddPasses,授权后调 addPasses(_:) 就能把 pass 直接塞进 Wallet,只用一条通知告知用户。对月票、通勤卡、频繁出差的航司 App 来说,省去了”打开 App → 找到订单 → 点添加到 Wallet”这条路径里至少两次点击。
详细内容
多场次票务的 pass.json 结构(02:04)。Upcoming events 写在 pass.json 顶层的 upcomingPassInformation 数组里,每个元素是一个完整的事件对象:
{
"upcomingPassInformation": [
{
"type": "event",
"identifier": "tour-2025-tokyo",
"displayName": "Tokyo Night",
"date": "2025-09-12T19:00:00+09:00",
"isActive": true,
"semantics": {
"venueName": "Tokyo Dome",
"venuePlaceID": "...",
"venueLocation": { "latitude": 35.7056, "longitude": 139.7519 },
"seats": [{ "seatSection": "A", "seatRow": "12", "seatNumber": "8" }]
},
"URLs": {
"purchaseParkingURL": "https://example.com/parking/tokyo"
},
"images": {
"headerImage": {
"1x": "https://example.com/[email protected]",
"2x": "https://example.com/[email protected]",
"3x": "https://example.com/[email protected]"
}
}
}
]
}
关键点:
identifier必须在 pass 内全局唯一且稳定,后续要靠它在 pass update 中增删或更新这一场。isActive在场次开始时置 true、结束后置 false,控制这一场在 Wallet 列表里是否高亮为”即将到来”。semantics里的venueName/venuePlaceID/venueLocation直接驱动详情页顶部的场馆卡片和地图按钮,结构与单场 Poster Event Ticket 完全一致——但不会自动继承外层 pass 的同名字段,需要在每个事件里重复填。images.headerImage必须提供 1x/2x/3x 三种分辨率,因为 upcoming event 详情页同时在 iOS 和 watchOS 上显示。- 若希望本场复用外层 pass 的场馆地图,把
venueMap.reuseExisting设为 true,否则 Wallet 不会显示场馆地图。
升级版登机牌的语义层(09:00)。Wallet 用三个语义字段定位航班、用 boarding/departure 时间差自动维护登机时间。pass.json 中只需提供原始字段,剩下交给 Apple Flight Service:
{
"semantics": {
"airlineCode": "CA",
"flightNumber": "983",
"originalDepartureDate": "2025-09-12T08:30:00+08:00",
"currentDepartureDate": "2025-09-12T08:30:00+08:00",
"originalBoardingDate": "2025-09-12T07:50:00+08:00",
"currentBoardingDate": "2025-09-12T07:50:00+08:00",
"passengerServiceSSRs": ["SVAN", "WCOB"],
"airlineLoungePlaceID": "..."
},
"purchaseLoungeAccessURL": "https://example.com/lounge"
}
关键点:
airlineCode + flightNumber + originalDepartureDate是三件套,缺一项 Wallet 就无法在 Apple Flight Service 里找到航班,整个自动追踪降级为静态显示。originalDepartureDate永远是机票上写的那个时间,不要因为延误就去改它;延误请只更新currentDepartureDate(09:45)。originalBoardingDate和originalDepartureDate在加票瞬间被用来计算”登机时长”,之后只要起飞时间被 Apple 推动,登机时间会按这个差值自动平移;想自己覆盖时用currentBoardingDate。passengerServiceSSRs接受 IATA 标准的 SSR 码(如SVAN服务犬、WCOB机上轮椅),文本由 Wallet 渲染,跨航司展示一致。airlineLoungePlaceID配合purchaseLoungeAccessURL出现时,Wallet 在按钮下额外渲染地图预览(14:56);只给 URL 而不给 placeID,按钮会显得很孤立。
自动添加通行证 API(17:35)。整套流程类似请求推送权限,但只能弹一次:
import PassKit
import SwiftUI
struct AddPassToWalletButton: View {
let pass: PKPass
var body: some View {
Button("Add to Wallet") {
Task {
try? await PKPassLibrary().requestAuthorization(for: .backgroundAddPasses)
}
}
.task {
let status = PKPassLibrary().authorizationStatus(for: .backgroundAddPasses)
if status == .authorized {
try? await PKPassLibrary().addPasses([pass])
}
}
}
}
关键点:
requestAuthorization(for: .backgroundAddPasses)是异步、一次性的;用户点过同意或拒绝后再调用,系统不会再弹(18:12),所以调用时机要选在用户大概率会同意的瞬间,比如刚完成购票。authorizationStatus(for:)不会触发 UI,可在.task或onAppear里直接读。addPasses(_:)接受数组,一次能添加多张——很适合”买年票一次发 12 张月卡”这种场景。- 一旦授权,用户可在系统设置里随时关掉这个能力,App 内无法阻止,授权状态需要每次校验。
- 后台添加成功后 Wallet 会发系统通知,不要再叠加自己的 in-app toast,避免重复打扰。
核心启发
-
做什么:把现有的”一场一票”模型迁移到
upcomingPassInformation。 为什么值得做:用户钱包里散落的 20 张同一球队季票会被收成 1 张,详情页可以承载演出嘉宾、场馆地图、停车券等所有相关信息,提高单张 pass 的留存与触达率。 怎么开始:先在测试 pass 里加 2-3 个事件验证 Wallet 渲染效果,确认identifier在 pass update 中的增删行为符合预期,再批量改后端模板。 -
做什么:为登机牌补齐语义层,启用 Apple Flight Service 追踪。 为什么值得做:可以下线自家”航班变更推送”那条链路(轮询 + APNs + 同步),并自动获得 Live Activity 和分享给亲友的能力,工程上是减法,体验上是加法。 怎么开始:第一步只补
airlineCode、flightNumber、originalDepartureDate、currentDepartureDate四个字段;第二步再补 SSR 码和服务 URL;上线后用真实航班对照 Apple Flight Service 的数据准确度,国内航线建议先双通道并行一段时间再考虑下线自家推送。 -
做什么:在购票成功的瞬间请求
backgroundAddPasses授权。 为什么值得做:把”加入 Wallet”的成功率从两步交互的衰减率拉满,对月票、通勤卡、连锁酒店会员卡这种”用户预期就是要装进 Wallet”的场景收益最大。 怎么开始:先在购票完成页加一个明确的”自动加入 Wallet”开关,让用户主动触发授权请求;不要在 App 启动时静默调用,那是浪费唯一一次弹窗机会。 -
做什么:用
passengerServiceSSRs标准化乘机标签。 为什么值得做:服务犬、轮椅、特殊餐食这些信息走 IATA 标准码,跨航司在 Wallet 里展示一致,能减少地勤反复问询,也省去自己维护一套本地化字符串。 怎么开始:把后端订单系统里的特殊请求字段映射到 IATA SSR 码表,写入 pass.json 的semantics.passengerServiceSSRs数组即可。
关联 Session
- Discover Apple-Hosted Background Assets — Apple 托管的后台资源下载,适合发布大体量内容时减轻服务器压力
- Dive deeper into Writing Tools — 系统级写作工具的深度接入,让 App 文本输入获得校对与改写能力
- Dive into App Store server APIs for In-App Purchase — App Store 服务端 API 与通知机制的最新更新
- Enhance child safety with PermissionKit — 用 PermissionKit 为儿童设备增强通讯安全
评论
GitHub Issues · utterances