其他
在 iOS/iPadOS 上通过 FinanceKit…
当需要从 Apple Wallet 安全访问 Apple Card、Apple Cash、Savings 或英国开放银行账户数据,并处理授权、查询、历史同步、订单和后台投递时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 35/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 39/100 ×0.15
适用场景
在 iOS/iPadOS 上通过 Fin特定
解决思路
- 申请:通过 Apple 的 FinanceKit entitlement request form 申请 com.apple.developer.financekit;这是 managed capability,Apple 逐应用审核。
- 使用组织级 Apple Developer 账号,个人账号不符合。
- 由 Account Holder 角色请求 entitlement。
- 确保 App 符合资格:Finance 分类、通过美国或英国 iPhone App Store 分发、提供净资产/支出/预算等财务管理功能。
- entitlement 按 bundle ID 批准;不要假设兄弟 App 或扩展自动继承。
- 如果 App 直接或通过受监管机构提供金融产品,必须允许客户把这些账户连接到 Apple Wallet 并与 FinanceKit 共享数据。
- Apple 批准后,在 Xcode 的 managed capabilities 中添加 FinanceKit entitlement。
- 在 Info.plist 添加 NSFinancialDataUsageDescription;该字符串在授权提示中展示,例如说明用财务数据跟踪支出并提供预算洞察。
- iOS 26 后台投递时,给主 App 和扩展 target 都添加 FinanceKit entitlement,并使用 App Groups 做共享存储。
- 美国数据需 iOS/iPadOS 17.4+,覆盖符合条件的 Apple Card、Apple Cash、Savings;排除 Apple Card Family 参与者和 Apple Cash Family 儿童。
- 英国数据需 iOS/iPadOS 18.4+,通过 open banking 支持符合条件的机构。
- Orders API 与 financial-data query API 分开可用。
- 调用任何财务数据 API 前,先检查 `FinanceStore.isDataAvailable(.financialData)`;该值跨启动和 iOS 版本恒定;不可用时不要调用其他 API,否则框架会终止 App。
- 查 Wallet orders 前先检查 `FinanceStore.isDataAvailable(.orders)`。
- `isDataAvailable` 返回 true 不保证设备上有数据;访问可能临时受限,如 Wallet 不可用或 MDM 限制;受限时抛出 `FinanceError.dataRestricted`,不会终止。
- 用 `FinanceStore.shared.requestAuthorization()` 请求授权;系统显示账户选择器,用户选择共享哪些账户及最早交易日期。
- 处理授权状态:`.authorized` 可继续查询,`.denied` 用户拒绝,`.notDetermined` 未做出有意义选择,`@unknown default` 兜底。
- 用 `store.authorizationStatus()` 查询当前授权状态且不弹窗。
- 授权或拒绝后再次调用 `requestAuthorization()` 会返回缓存决定且不再展示提示;用户可在 Settings > Privacy & Security > Financial Data 更改。
- 账户模型:enum 有 `.asset`(Apple Cash、Savings 等)和 `.liability`(Apple Card 信用等);共同属性 `id`、`displayName`、`institutionName`、`currencyCode`,liability 额外有信用字段。
- 用 `AccountQuery` 查询账户:设置 `sortDescriptors`(如按 `Account.displayName`)、`predicate`、`limit`、`offset`,调用 `store.accounts(query:)`。
- 处理账户类型:`switch account` 到 `.asset` 读取 `currencyCode`;到 `.liability` 检查 `creditInformation.creditLimit` 并读取 `amount`、`currencyCode`。
- 余额表示某时点金额;`CurrentBalance` 是 `.available`(含 pending)、`.booked`(仅已入账)、`.availableAndBooked` 三种之一。
- 用 predicate 按 `balance.accountID == accountID` 过滤,构造 `AccountBalanceQuery`,设置 `sortDescriptors`、`predicate`、`limit`、`offset`,调用 `store.accountBalances(query:)`。
- 余额金额总是正数 decimal;用 `creditDebitIndicator` 判断符号:`.debit` 前加负号,否则不加;从 `CurrentBalance` enum 提取 `.available`、`.booked`、`.availableAndBooked` 的 `Balance` 后格式化。
- 用 `TransactionQuery` 查询交易:包含 Swift predicate、sort descriptors、limit、offset;例如按 `accountID` 过滤、按 `transactionDate` 倒序、limit 50,调用 `store.transactions(query:)`。
- 读取交易:`transactionAmount` 是正数;用 `creditDebitIndicator == .debit` 判断为 spent,否则 received;`merchantName`、`merchantCategoryCode`、`foreignCurrencyAmount` 可选。
- 使用内置 predicate 辅助:`TransactionQuery.predicate(forStatuses: [.booked])` 过滤已入账;`TransactionQuery.predicate(forTransactionTypes: [.pointOfSale, .directDebit])` 过滤类型;`TransactionQuery.predicate(forMerchantCategoryCodes: [MerchantCategoryCode(rawValue: 5411)])` 过滤商户类别。
- 交易字段表和更多查询模式读 references/financekit-patterns.md。
- 长查询/历史:用基于 AsyncSequence 的 history API 做 catch-up sync、实时更新或可恢复同步;返回 inserted、updated、deleted 的 ID 与 `HistoryToken`。
- catch-up 交易:调用 `store.transactionHistory(forAccountID:since:isMonitoring:false)`,在 `for try await changes` 中先删除 `changes.deleted` 对应本地记录,再 upsert `changes.inserted + changes.updated`,最后保存 `changes.newToken`。
- 持久化 `HistoryToken`:它符合 `Codable`;用 `JSONEncoder` 编码后存 `UserDefaults`,用 `JSONDecoder` 读取,以便恢复查询且不重复处理数据。
- 如果保存的 token 指向已压缩历史,框架抛出 `FinanceError.historyTokenInvalid`;丢弃 token,并立即对受影响 account 或 balance stream 跑新的 catch-up 查询,以重建本地状态和替代 token。
- 只有需要独立实时监听时才用 `isMonitoring: true`。
- 账户和余额历史:调用 `store.accountHistory(since: nil, isMonitoring: true)` 和 `store.accountBalanceHistory(forAccountID: accountID, since: nil, isMonitoring: true)`。
- 持续预算同步应覆盖用户授权的数据模型:account objects 处理账户新增/移除,account balances 处理趋势和 widget 状态,transactions 处理支出明细。
- 每个 stream 或 account 使用独立 history token,避免一个被压缩 token 导致所有数据重同步,只重同步受影响 scope。
- 对需要选择性、临时访问且不需要完整授权的 App,使用 FinanceKitUI 的 `TransactionPicker`;访问不持久化,交易直接传入供立即使用。
- `TransactionPicker` 实现:在确认 `FinanceStore.isDataAvailable(.financialData)` 后,使用 `TransactionPicker(selection: $selectedTransactions)` 并提供 `Label` 按钮。
- FinanceKit 支持保存和查询 Wallet orders,如购买收据、物流跟踪。
- 保存订单:调用 `store.saveOrder(signedArchive: archiveData)`;处理 `.added`(已保存)、`.cancelled`(用户取消)、`.newerExisting`(Wallet 中已有更新版本)、`@unknown default`。
- 检查已有订单:用 `FullyQualifiedOrderIdentifier(orderTypeIdentifier: com.merchant.order, orderIdentifier: ORDER-123)` 和 `store.containsOrder(matching: orderID, updatedDate: lastKnownDate)`;处理 `.exists`、`.newerExists`、`.olderExists`、`.notFound`。
- 添加订单到 Wallet 按钮:用 FinanceKitUI 的 `AddOrderToWalletButton(signedArchive: orderData)`,在回调处理 `.success(SaveOrderResult)` 或 `.failure(Error)`。
- iOS 26+ 支持 background delivery extension,在 App 生命周期外通知财务数据变化;主 App 中的用户授权会被扩展继承。
- 主 App 和扩展 target 都需要 FinanceKit entitlement;用 App Groups 在 App、扩展和相关 widget 间共享数据。
- 启用后台投递:调用 `store.enableBackgroundDelivery(for: [.accounts, .accountBalances, .transactions], frequency: .daily)`;这些注册方法同步且不抛错,不要写 `try` 或 `await`。
- 可用频率:`.hourly`、`.daily`、`.weekly`,表示数据变化时扩展启动之间的预期最小间隔;更长频率给扩展更大的处理窗口。
- 禁用后台投递:选择性用 `store.disableBackgroundDelivery(for: [.transactions])`,全部用 `store.disableAllBackgroundDelivery()`。
- 在 Xcode 创建 Background Delivery Extension target;直接在扩展类型上实现两个 async 入口;只有在必要工作保存后才从 `didReceiveData(for:)` 返回。
- 在 `didReceiveData(for:)` 中按类型处理:包含 `.transactions` 时处理新交易,包含 `.accountBalances` 时更新余额缓存,包含 `.accounts` 时刷新账户列表;在 `willTerminate()` 中保存部分工作。
- 不要在没有检查数据可用性时调用 API;先 guard `FinanceStore.isDataAvailable(.financialData)`,不可用则显示提示并返回。
- 不要把金额当成有符号值;金额总是正数,需根据 `creditDebitIndicator` 的 `.debit` 变负、否则为正。
- 不要假设授权访问会一直持续;捕获 `FinanceError`,遇到 `.dataRestricted` 时显示受限消息并优雅处理。
- 不要用快照替代可恢复 history;使用标准 transaction-history 循环,且仅在本地删除和 upsert 提交后持久化每个 `newToken`;保持每个 stream/account 独立 token;遇到 invalid-token 只重同步受影响 scope。
- 不要误解 liability account 的 credit/debit:asset 和 liability 的 `.debit` 都表示资金流出;`.credit` 在 asset 表示收到钱,在 liability 表示还款或退款并增加可用信用;完整解释表见 references/financekit-patterns.md。
- 只查询 App 真正需要的数据,避免过度访问。
- 用户撤销访问时,删除本地保存的财务数据。
- 审查时确认:在任何 API 调用前检查 `FinanceStore.isDataAvailable(.financialData)`。
- 审查 App 资格:Finance 分类、美国或英国 iPhone App Store 分发、财务管理功能、组织账号、Account Holder 请求。
- 审查 `com.apple.developer.financekit` entitlement 已按 app bundle ID 请求并批准。
- 审查 Info.plist 的 `NSFinancialDataUsageDescription` 清晰具体。
- 审查授权状态处理覆盖 `.authorized`、`.denied`、`.notDetermined`。
- 审查捕获并优雅处理 `FinanceError.dataRestricted`。
- 审查正确应用 `CreditDebitIndicator`,不把金额当有符号。
- 审查为可恢复查询持久化 history tokens。
- 审查 `FinanceError.historyTokenInvalid` 时丢弃 token 并立即重同步受影响 stream。
- 审查持续同步计划覆盖授权 accounts、balances 和 transactions,而不只是 transactions。
- 审查长运行查询在不需要实时更新时使用 `isMonitoring: false`。
- 审查在不需要完整授权时使用 transaction picker。
- 审查只查询 App 真正需要的数据。
- 审查从 history changes 中的 deleted IDs 显式从本地 account、balance、transaction 存储删除。
- 审查后台投递调用使用同步 iOS 26 API,扩展与主 App 在同一 App Group。
- 审查后台投递注册所有需要的数据类型:`.accounts`、`.accountBalances` 和/或 `.transactions`。
- 审查 FinanceKit entitlement 同时添加到主 App 和 background delivery extension targets。
- 审查用户撤销访问时删除财务数据。
- 参考扩展模式:predicates、sorting、pagination、currency formatting、background updates 见 references/financekit-patterns.md。
适用边界 · 注意事项
- 不适用于非 Apple 平台、无组织级 Apple Developer 账号或无法获得 com.apple.developer.financekit managed entitlement 的项目。
- 不适用于不符合资格或不在美国/英国 App Store for iPhone 分发的 App;也不适用于仅支持低于 iOS/iPadOS 17.4 的目标,TransactionPicker 需 18,后台投递需 26。
- Apple Pay 结账应使用 PassKit,Widget UI/timeline 应使用 WidgetKit,Wallet 订单跟踪邮件或 Apple Business Connect 优化不属于本技能。