WWDC Quick Look 💓 By SwiftGGTeam
Meet FinanceKit

Meet FinanceKit

元の動画を見る

Highlight

FinanceKit は iOS 18 で導入された新フレームワークで、Apple Wallet の金融データにアクセスする標準化 API を提供します。Apple Card、Apple Savings、Apple Cash のデータをローカルストアに集約し、すべてのデータ処理は端末上で完結、ネットワーク接続は不要です。


主要内容

家計管理アプリ最大の課題はデータが取得できないこと。各銀行の API 形式が統一されず、接入手順も様々で、多くはオープンインターフェースすらありません。ユーザーは手動で取引を入力し、体験が悪く、データも誤りやすい。

FinanceKit がこの状況を変えます。Apple Wallet から直接金融データを読み取り—Apple Card、Apple Savings、Apple Cash の口座、残高、取引記録をすべて 1 つのローカルストアに集約。ネットワーク不要、すべて端末上で処理。複数金融機関との連携は不要—FinanceKit 1 つのフレームワークだけで済みます。

データアクセスには 3 方式があり、権限は弱い順です。最も軽量なのは Transaction Picker—システム UI コンポーネントでユーザーが手動で共有する取引を選択。認可は一時的。次はクエリ API で、ユーザーの明示的認可が必要。特定条件の金融データを一括取得。最後は長期接続クエリ API で、Swift Async Sequences ベース。金融データの変更を継続監視し、History Token で断点再開—アプリ再起動後も前回位置から同期を継続。

詳細

データ可用性チェック

FinanceKit API を呼ぶ前に必ず isDataAvailable をチェック。false のまま呼び続けるとフレームワークがアプリを強制終了—推奨ではなく必須(05:38)。

import FinanceKit

let available = FinanceStore.isDataAvailable(
    .financialData
)

guard available else {
    // No meaningful action can be performed
    return
}

キーポイント:

  • FinanceStore.isDataAvailable(.financialData) は静的メソッド。インスタンス不要
  • false のとき他の FinanceKit API を絶対に呼ばない。さもないとアプリ終了
  • 同じ iOS バージョン内では変化しない。キャッシュ可能

Transaction Picker

ユーザーに数件の取引を選ばせるだけなら(経費精算など)、Transaction Picker が最もシンプル(08:08)。

import SwiftUI
import FinanceKit
import FinanceKitUI

struct TransactionSelector: View {
  @State private var selectedItems: [FinanceKit.Transaction] = []

  var body: some View {
    if FinanceStore.isDataAvailable(.financialData) {
      TransactionPicker(selection: $selectedItems) {
        Text("Show Transaction Picker")
      }
    }
}

キーポイント:

  • FinanceKitUI の追加 import が必要
  • selectionTransaction 配列にバインド。ユーザー選択で直接反映
  • クロージャにトリガーボタンの ViewBuilder
  • 認可は一時的(ephemeral)。Picker は選択を記憶しない

認可とクエリ API

クエリ API はユーザーの明示的認可が必要。Info.plist に NSFinancialDataUsageDescription を追加し、requestAuthorization() を呼ぶ(12:16)。

import FinanceKit

let store = FinanceStore.shared

guard store.isDataAvailable(for: .financialData) else {
    return
}

let authStatus = await store.requestAuthorization()

guard authStatus == .authorized else {
    return
}

キーポイント:

  • requestAuthorization() でシステムダイアログが表示。ユーザーが共有する口座を選択
  • 認可後も設定でいつでも変更・取り消し可能
  • App Store 配布には追加の distribution entitlement が必要

口座クエリ

認可後、AccountQuery で口座をクエリ(15:24)。

let store = FinanceStore.shared

let sortDescriptor = SortDescriptor(\Account.displayName)

let predicate = #Predicate<Account> { account in
   account.institutionName == "Apple"
}

let query = AccountQuery(
   sortDescriptors: [sortDescriptor],
   predicate: predicate
)

let accounts : [Account] = try await store.accounts(query: query)

キーポイント:

  • Swift の #Predicate マクロでフィルタ条件を構築
  • SortDescriptor でソート制御。少なくとも 1 つ指定を推奨
  • 各データ型に対応する Query クラスがある

残高クエリ

残高クエリは返却数を制限可能。トレンドグラフ描画に適する(18:12)。

func getBalances(account: Account) async throws -> [AccountBalance] {

    let sortDescriptor = SortDescriptor(\AccountBalance.asOfDate, order: .reverse)

    let predicate = #Predicate<AccountBalance> { balance in
        balance.available != nil &&
        balance.accountId == account.id
    }

    let query = AccountBalanceQuery(
        sortDescriptors: [sortDescriptor],
        predicate: predicate,
        limit: 7
    )
    return try await store.accountBalances(query: query).reversed()
}

キーポイント:

  • 日付降順で最新 7 件を取得し、reversed() で時系列順に復元
  • asOfDate は残高の計算時刻。データの鮮度判断に使用
  • 金額は常に正数。方向は creditDebitIndicator で区別

長期接続クエリと断点再開

transactionHistory は Async Sequence を返し、変更を継続的にプッシュ(20:27)。

import FinanceKit

let store = FinanceStore.shared
let account: Account = ...

let transactionSequence = store.transactionHistory(
    forAccountID: account.id
)

for try await change in transactionSequence {
    processChanges(change.inserted, change.updated, change.deleted)
}

since パラメータに前回保存した History Token を渡せば、チェックポイントから再開(21:04)。

import FinanceKit

let store = FinanceStore.shared
let account: Account = ...
let currentToken = loadToken()

let transactionSequence = store.transactionHistory(
    forAccountID: account.id,
    since: currentToken
)

for try await change in transactionSequence {
    processChanges(change.inserted, change.updated, change.deleted)
    persist(token: change.newToken)
}

キーポイント:

  • 各 change に insertedupdateddeleted の 3 配列
  • change.newToken は Codable で、シリアライズしてディスクに保存可能
  • since: token を渡すとその token 以降の増分変更のみ返却
  • リアルタイム監視が不要なら isMonitoring: false を追加。既存変更処理後 Sequence は自動終了

重要ポイント

  • やること:Transaction Picker で「手動取引インポート」機能を実装。価値:Info.plist 説明、認可ダイアログ、entitlement 申請が不要。最低コストで統合。始め方FinanceKitUI を import し、ビューに TransactionPicker を配置、@State 配列にバインド。

  • やること:History Token で増分同期を実装。価値:起動のたびに全取引を全量取得するとデータ量が多い場合に遅く無駄。Token で増分のみ処理し、パフォーマンスが大幅向上。始め方:change 処理後に change.newToken をディスクに永続化。次回起動時に transactionHistory(forAccountID:since:) に渡す。

  • やること:口座タイプに応じて金額方向を正しく解釈。価値:金額は常に正数。debit/credit の意味は口座タイプ依存—資産口座(Apple Cash)の debit は残高減少、負債口座(Apple Card)の debit は利用可能枠減少。混同するとユーザーに誤った収支統計を表示。始め方:取引リスト表示時に口座が資産か負債かを判断し、creditDebitIndicator と組み合わせて収入/支出を決定。

  • やること:データ制限状態の変化を監視。価値:データ可用性(isDataAvailable)は定数だが、データ制限(restriction)はアプリ実行中に変化する可能性—MDM ポリシー調整など。フレームワークはエラーを投げるがアプリは終了しない。自行処理が必要。始め方:catch 分岐で制限エラーを処理し、現在金融データにアクセスできない旨をユーザーに通知。

関連セッション

コメント

GitHub Issues · utterances