ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
FinanceKitについて
FinanceKitを財務管理アプリで使用すると、ユーザーの同意を取得しコントロールを提供しつつ、Apple CashやApple Cardなどに関連するデバイス上のデータをシームレスかつ安全に共有できます。口座、トランザクション、残高などの情報への1回限りまたは継続的なアクセスをリクエストする方法のほか、iOSおよびiPadOSで優れた体験を実現する方法についても説明します。
関連する章
- 0:00 - Introduction
- 1:48 - Overview of data types
- 5:03 - Access financial data
- 21:56 - Best practices
リソース
-
ダウンロード
こんにちは Antonioです Apple Walletチームの iOSエンジニアです 現代では 家計の管理が 重要な役割を持っています クレジットカードの支払額に対して 自身の収入が十分か または 今月はカプチーノ代に いくら払っているかなど 自身の財務データに いつでもアクセスできる必要があります そこで FinanceKitの出番です
FinanceKit APIは Appleウォレットに保存された 財務データの中央リポジトリへの アクセスを提供します アクセスする財務データは すべてデバイス上のローカルデータであり アクセスにインターネットを必要としません また FinanceKitは Apple Card、Savings Apple Cashなどから 財務データを集計します また プライバシー保護策として アプリがアクセスできる財務データの 種類と金額を データ所有者が 明示的に管理できます FinanceKitは 魅力的な財務アプリの構築に使用できる 充実した財務データを提供します 例えば各口座情報の概要 最新の取引履歴を含む 利用可能残高の情報 入出金や 利用可能な借入枠の変更などの 金融取引の情報などです これらの履歴も確認できます 一緒に確認していきましょう 本セッションでは FinanceKitの概要と 財務データにアクセスして表示する上で 理解しておくべきことを説明します まず FinanceKit APIが持つコア構造の 概要を見ていきます 次に Appleウォレットに保存される 豊富な財務データに このAPIを使用して アクセスする方法を説明します 最後に アプリにFinanceKitを統合する際に 留意すべきベストプラクティスを いくつかお伝えします
まずは みなさんが利用できる 主な要素の概要から説明します FinanceKitは 主に3つのデータ型を使用して 財務データをモデル化します その基礎は口座です 当座預金口座やクレジットカード Apple CardによるSavingsなどが 該当します すべての口座には残高があります 大まかにいうと 残高は特定の時点で 口座にある金額を指します 口座には 残高のほかに トランザクションがあります 端的にいうと これは口座への入出金を指します FinanceKitは これらのモデルに生じたすべての変化を ユーザーによる口座開設の時点から 金融データの処理に関する 現地の法令に準じてトラッキングします トランザクションは構造体であり 有用な一連のフィールドで構成されます フィールドの一つであるIDは すべてのトランザクションに対し 一意であり かつ 各デバイスに対しても一意です このIDは特に トランザクションの 経時変化をトラッキングする上で有用です 大半のトランザクションには ISO 18245に準拠した マーチャント(加盟店) カテゴリコードがあります これは アプリで支出カテゴリを トラッキングする場合に役立ちます また 一部の財務データでは マーチャント名もあります
すべてのトランザクションには オリジナルの説明があります これは金融機関が提供するコードです 一部の財務データでは 分かりやすい表示用の説明もあります トランザクションには 必ずトランザクションの日付があります また 金融機関から受領したステータスと 情報の詳細度に基づき FinanceKitは トランザクションの投稿日を提供します 最後に 通貨コードと 十進数の値で構成される 金額のデータがあります トランザクションが国外で発生した場合 金融機関から 金額がその国外通貨で提供されます ここで重要なのは FinanceKitでは 金額に対し 通貨の換算を行わない点です FinanceKitのデータは 金融機関から 受領した金額と通貨のままで提供され 借方であるか貸方であるかに関係なく 正の十進数の値として保存されます では トランザクションが貸方であるか 借方であるかを どのように判断するのでしょうか そのために 貸方借方を示すインジケータがあります すべてのトランザクションに 口座への入金であるか 出金であるかを示す creditDebitIndicatorがあります 当然ですが このインジケータに可能な値は debitまたはcreditです しかし 注意すべき点は この値の解釈が 口座の種類によって変わることです
トランザクションが借方であれば 資産勘定では 口座残高が減少します 例えば Apple Cashなどの トランザクションです また 負債勘定のトランザクションでは 借入枠が減少します 例えばクレジットカードなどです トランザクションの creditDebitIndicatorが貸方であれば そのトランザクションによって 資産勘定であれば残高が増加し 負債勘定であれば借入枠が増加します 以上 FinanceKitで使用する 最も重要な型を簡単にご紹介しました 詳細については 利用できるすべての クラス 構造体 フィールドを 網羅したドキュメントをご参照ください 必要な基礎を説明できたので エキサイティングなパートに移りましょう これらの型で表現される財務データに FinanceKit APIでアクセスする方法です 財務データへのアクセスは 主に3つの部分で構成されています データを利用できるかどうかの判断 ユーザーが選択した財務データに アクセスするためのピッカーの使用 クエリ用のAPIを使用した 財務データへの 1回限りまたは長期的なアクセスです では 目的のデバイスが FinanceKitをサポートしているかを判断し また サポートしている場合 制限対象の財務データの有無の 判断から始めましょう 財務データが利用可能か確認するには まずFinanceKitをインポートします 次に FinanceStoreクラスの isDataAvailableメソッドを使用して データが利用できるか確認し financialDataの 列挙型のケースを渡します フレームワークがfalseを返した場合 財務データに関連する ほかの呼び出しはできません その場合 強いシグナルを送るために フレームワークはアプリを終了します 一方 フレームワークがtrueを返した場合 この値が アプリの起動ごとに変化せず 複数のiOSバージョンでも 一貫していると見なせることが保証されます
ただし trueが返されても デバイス上で実際のデータを 必ずしも利用できるとは限りません 財務データが存在しても デバイスの種類や設定によっては アクセスが制限されることがあります データを恒常的に利用可能と見なせる場合も データへのアクセス制限は変動します アプリがフォアグラウンドで動作していても 呼び出しごとに制限は変化し得ます
例えば Appleウォレットアプリが 利用できないことや 企業のモバイルデバイス管理システムで Appleウォレットへのアクセスが 制限されていることがあります
データが制限されるとフレームワークから エラーがスローされますが アプリは終了せず データが制限されていることが示されます この時点で 財務データに 現在アクセスできないことを アプリがユーザーに通知します
財務データが利用可能であることが わかっていれば その財務データにアクセスできます これを迅速かつ容易に実現するには アプリのユーザーが 共有したいトランザクションを FinanceKitの トランザクションピッカーで 選択できるようにします この新しいビューでは 利用できるトランザクションを 選択するためのリストを いつアプリに表示するか決定できます アプリのユーザーは トランザクションのリストを表示して アプリからのアクセスを許可する 対象を選択します デフォルトでは トランザクションは 時系列順に表示されますが 画面上部の検索フィールドに入力した フリーテキストに基づく絞り込みや 入力中に提示されたトークンを使用した 絞り込みもできます なお アプリからトランザクションへの アクセスは トランザクションの共有後 すぐに可能になりますが このアクセス許可は一時的なものです 共有されたトランザクションは 直接アプリに渡されて すぐに使用され ピッカーには記憶も保存もされません ピッカーの使用方法は非常に簡単です PhotosPicker APIの 使用経験があれば Transaction Picker APIにも なじみやすいでしょう 極めてシンプルな方法で FinanceKitやFinanceKitUIに関連する UIフレームワークをインポートできます 次に 選択したトランザクションを 保持する変数を宣言します この例では ViewのStateプロパティを使用します すでにお話ししたように 財務データが利用可能かどうかは アプリ側で判断する必要があります この例では Viewのbodyで isDataAvailableを呼び出し selectedItemsのstate変数を 1つ目のパラメータとして TransactionPickerビューに渡します 2つ目のパラメータとして ViewBuilderクロージャを渡します この例では このクロージャは単に ピッカーの表示を トリガするボタンのラベルです この数行のコードだけで タップすると トランザクションピッカーを表示し 選択したトランザクションを アプリで使用可能にするボタンができます このトランザクションピッカーは 出張旅費をトラッキングする アプリなどに最適です または 特定のトランザクションへのアクセスを アプリユーザーが選んで 許可できるようにする場合や あらゆるトランザクションへのアクセスを 私用のものも含め 無制限に許可する場合にも有効です ただ アプリの中には当然ながら ウォレットの財務データに フルアクセスできると 極めて便利なものがあります
財務データへのフルアクセスに使用できる クエリのAPIの詳細を見てみましょう このAPIは 継続的なユースケースにも データのスナップショットにも対応します クエリのAPIは非同期的に動作し トランザクションだけでなく FinanceKitで利用できる すべての種類の財務データへの アクセスを提供します 明白な許可なくデータが共有されないよう ユーザーの同意が必須ですが 承認が行われると アプリは永続的な接続を確立して 利用可能な財務データに 常時アクセス可能になります 便利なことに これらのAPIは iOS 17.4以降を搭載する iPhoneで利用できます FinanceKitでは 2種類の方法で データのクエリを実行できます
1つ目の方法では 現在の値に基き タイプごとに一連の財務データを 順序つきで返します この方法では プレディケート(述語) ソート記述子 上限値 オフセットの 各パラメータを利用でき 順序をつけた タイプごとの一連の財務データを 非同期で返して アプリで使用できるようにします もう1つの方法は 変更のみを対象とする APIで 機能が少し異なります 条件に一致した 財務データの配列を返すのではなく 取得したいデータに発生した 変更の履歴を返します これらのAPIは継続的に動作するため 金融機関から新しいデータが提供されると 変更のみに絞られた更新情報が アプリに取得されます 現在 クエリのAPIの使用には 追加の要件があるので ドキュメントを調べる前に ご自身のニーズに トランザクションピッカーで 対応できないか確認しましょう 要件の1つ目は ユーザーに表示する 原則的な利用方法の説明を アプリのInfo.plistに追加することです そのためには プライバシーの箇所にある 財務データの利用方法の説明を探すか NSFinancialDataUsageDescription キーを使用して 説明を手動で追加します
この説明を入力すると FinanceStoreクラスの 共有インスタンスに対して requestAuthorization関数を 呼び出すことにより 認可を コードにより リクエストできるようになります
認可をリクエストすると 財務データへのアクセス許可を求める システムアラートが表示されます 許可すると 共有する口座を 選択する画面が表示されます requestAuthorizationの 呼び出しの結果が grantedの場合 財務データへのクエリが許可されます なお ユーザーはこの許可をいつでも 設定から取り消せます リクエストの結果がdeniedの場合 アクセス取得のリクエストは却下されます また unknownが返されることもあります これは ユーザーが有効な選択を行う 機会がなかった場合に発生します requestAuthorizationの呼び出しでは ステータスが返されますが authorizationStatus関数では ユーザーへのプロンプトはなく 現在のステータスを返すだけです App Storeでアプリを 入手できるようにする場合は 配信エンタイトルメントを リクエストする必要があります このリクエストのための 効率的なプロセスを利用できます 本セッションの関連リソースに これに関する情報ページへの リンクがあります 認可リクエストのプロセスを コードで見てみましょう FinanceKitのインポート後 財務データの有無を確認し ある場合のみ 以降の処理に進みます 共有のFinanceStoreインスタンスに対し 認可リクエストを呼び出します 該当するシステムダイアログがある場合 アプリ画面の上部に表示されます ユーザー側で選択を行うと ダイアログが閉じ この非同期関数から アプリに新しいステータスが返されます
ユーザーが財務データへのアクセス権を 付与したか確認し 先へ進むことができます
先ほど見た通り アプリからの認可リクエストに対し ユーザーが意思表示しない場合 システムアラートが表示されます ユーザーがを押すと 認可はdeniedのステータスを受け取ります 一方 を押すと OSが選択可能な口座のリストを表示します ユーザーはどの口座を共有するかを このリストから選択して 口座ごとに アプリで利用できるアクティビティを 共有の開始時点も含め指定できます ユーザーの考えが変わった場合 を押せば アプリは 次に必要が生じた際 再度認可をリクエストします
一方 のボタンを押すと 選択が確定され 制御がアプリに渡されます
ほかの多くのフレームワークと同様に ユーザーが アクセス権の付与または拒否を選択すると 同じ認可リクエストがあった際 再度の選択は求められません ただし ユーザーが後で考えを変えた場合は から 財務データへのアクセスを変更できます 共有する口座を追加できるほか 共有の開始時点も変更できます これで アプリに対し ユーザーの財務データへの アクセス権が付与されたので データのクエリの方法を確認しましょう その前に 口座について 知っておくべきこととして FinanceKitによって公開される 主要な財務データタイプがあります その構造を説明します
各口座は 列挙型のケースとして モデル化され 全口座に共通のプロパティと その口座タイプに固有の プロパティを持ちます この構造のため FinanceKitは柔軟性が高く 各々の固有のプロパティで 各口座タイプをモデル化しつつ 共通の中核的要素を維持できます 共通のプロパティの1つは 一意のローカルIDです 口座IDはすべてのトランザクションと 残高にも存在するので これらの各オブジェクトを それぞれの口座に 容易に関連づけできます どの口座にも金融機関名があります 金融機関名は 口座が存在する金融機関の名前を表す 文字列です 表示名は ユーザーに表示される口座名です 金融機関によっては 口座の説明も提供されます 口座の主要通貨は 3文字の通貨コードで示されます これらはすべて 口座のモデルに共通のプロパティです 負債勘定の口座 例えば Apple Card口座のモデル化に 使用している口座などの場合は 追加のプロパティがあります 借り入れできる最大額を示す利用限度額 次回決済日 最小決済額などのほか 期限切れの決済額があれば それも含まれます 口座をモデル化する方法がわかったので FinanceStoreから 口座のクエリを実行しましょう
まず ソート記述子として Account.displayNameを定義します クエリではソート記述子は 必須ではありませんが 少なくとも1つは 使用することをおすすめします 次にSwiftの新しい プレディケートマクロを使用して プレディケートを定義します この例では Apple Card、Apple Cash およびSavingsに口座をリンクします
次に ソート記述子とプレディケートを 引数として渡すクエリを作成します ご覧のように どの財務データの型にも クエリが関連づけられています 最後に クエリを実行して 一致する口座をストアから取得します 以上 口座について確認したので 次は残高に関する処理を見てみましょう すでに説明したように どの口座にも残高が少なくとも1つあり ユーザーがiOS 17.4に アップグレードした時期によっては 複数の残高があります
FinanceKitは 残高の履歴記録を保持します これは 例えば残高のトレンドなどの 残高の経時的変化を 表示するために使用できます まず 残高の構造を確認しましょう 残高は 3つのケースで構成される 列挙型としてモデル化されます Availableは 残高の生成時に 保留中のトランザクションを 算入するためのものです Bookedでは 投稿済みの トランザクションのみを算入します Available & Bookedには その名の通り 2つの残高を表示する 2つのフィールドがあります
この違いを除けば 残高のプロパティは ほかのすべての列挙型のケースと共通です 本セッションでご紹介した ほかの財務データの型と同様に 残高にも一意のローカルIDがあります 口座IDとともに 帰属する口座への 残高のマッピングに使用できます FinanceKitは 金融機関で残高が計算された日付も AsOfDateプロパティに記録します このプロパティを使用して 残高が最新であるかの確認もできます もう一つ重要なこととして 残高は 十進数の値と通貨コードを ペアにした 金額を1つ以上持ちます なお残高の金額については データを提供するのは金融機関であり フレームワークによる計算ではありません また この金額は正の値で保存されます そのため このフレームワークでは別途 creditDebitIndicatorプロパティで 残高の状態を区別します 資産勘定で 残高のcreditDebitIndicatorが debitであれば その残高はマイナスです 逆に 残高の値がcreditであれば 残高はプラスです 負債勘定の残高の creditDebitIndicatorがcreditなら その口座はその時点で 貸方になっています 一方 口座に支払い済みの残高があれば 口座は借方となります 残高の構造を説明したので データのクエリを実行して ご覧のような グラフを作成する方法を確認します このグラフは 最新の利用可能残高の 日ごとの変化を示しています getBalances関数で 最近の日付から遡って 残高を取得するために まず 逆日付順のソート記述子を定義します 次に 利用可能残高のみ表示されるように 結果を絞り込み さらに 目的の口座の 残高のみに絞り込みます
クエリの作成手順はすでにおなじみですが ここではソート記述子と プレディケートを使用するほかに 結果の表示件数も制限します
このクエリを実行して その結果が時系列順で表示されるように 取得した残高を逆日付順にします 以上はスナップショットクエリでしたが 次は継続的なクエリについて説明します 継続的なクエリが適しているのは デバイスがアップデートを受信するたびに リアルタイムでそのデータを利用する場合や アプリの起動ごとに クエリを再実行する場合です 継続的なクエリは Swift asyncの シーケンス上で作成するので 更新を簡単に 継続的にストリーミング配信できます 継続的なクエリが返すのは スナップショットクエリと同様に モデルの配列ではなく変化です この変化とは データベースの 前回の状態や初期状態からの差分です 変化には次の構成要素があります まずはモデル挿入の配列で 例えば 前回の更新以降の すべての新規トランザクションです 次はオブジェクト更新の配列です 例えば 負債勘定での 利用限度額の引き上げなどです オブジェクトIDの削除もあります これは ウォレットに存在しなくなった 財務データオブジェクトを アプリで削除する場合などです すべての変化では 履歴トークンも提供されます 履歴トークンは 特定の時点における 財務データストアのプロキシです 履歴トークンは不透明な構造体で Codableに準拠しているため 容易にシリアル化できます 履歴トークンを使用して 継続的なクエリを再開し 特定時点以降の ストアの更新を取得できます 空き領域とパフォーマンスの向上のために 履歴の変化をシステムで圧縮できます 一般的に このような圧縮はまれです FinanceKitでは 履歴を少なくとも 数か月は非圧縮で保持するためです ただし アプリで保存された最新のトークンに 相当する変化が圧縮されている場合 履歴クエリから エラーがスローされることがあります とはいえ これは問題ではありません フレームワークは データを失うことなく アプリが通常の動作に 復旧できるようにしつつ クエリを再開することができます 次は トランザクションに対するシンプルな 継続的クエリを作成する方法です FinanceKitをインポートした後 transactionHistory APIを使用して トランザクション履歴の 非同期シーケンスを定義してから transactionSequenceにおいて for awaitですべての変更をリッスンします これで それらの変更を 自由に処理できるようになります このサンプルの個人用財務アプリでは 起動時に全トランザクションを読み込みます 関連づけられた口座で 新しいトランザクションが取得されると ただちに画面に表示されます シンプルな継続的クエリの動作を 確認しました 次に再開可能な機能を使用してみます まず APIで新しい変更を処理するたびに 履歴トークンが ディスクなどの安全な場所に保存されます 保存された履歴トークンを使用するには 読み込む必要があります 次に 読み込んだ最新のトークンを sinceパラメータを使用して 履歴クエリに渡します この簡潔な変更により アプリの再起動ごとに 継続的なクエリを再開して イベントの重複を排除できるようになります 財務データの非同期シーケンスは 通常は終了することなく アプリが使用する更新データを リアルタイムで生成します アプリにおいて 再開可能なクエリのみが重要である場合 新たな変更をモニタリングするよりも 継続的なクエリにパラメータを渡す方が 優れたソリューションと言えます この方法では 既存の変更の処理が アプリですべて完了し次第 非同期シーケンスは終了します このような方法で デバイス上の財務データの 有無の判断とアクセス許可が行われます 最後に アプリに FinanceKitを統合する際に 留意すべきベストプラクティスを いくつかお伝えします 繰り返しになりますが 財務データは 誰にとっても機密性が高い情報です そのため アプリによる 財務データへのアクセスを許可した ユーザーの当社への信頼を 真摯に受け止めるべきです この信頼に応えるために ユーザーがデータを削除した場合や アプリによるアクセスを撤回した場合は 必ずデータを削除してください
たとえ可能であっても クエリによるデータアクセスは 必要最小限にしましょう トランザクションピッカーは当事者双方に 有益な 財務データへのアクセス方法です 貴重なデータが得られるとともに アプリに対する要件が少なく 同時に ユーザーは 安心して共有できる データのみをアプリに提供できます 財務データへのアクセスは ハイパフォーマンスな設計であり アプリが財務データすべてに アクセスできるなら いつでも非圧縮のスナップショットを 取得するのが 一見便利に思えます しかし より効率的なのは 再開可能な継続的クエリを使用して 最新の更新のみを処理する方法です アプリでの処理が必要なデータの量が 大幅に減少するためです 以上で FinanceKitを活用して 財務アプリを初めて構築する上で 必要なすべての情報を解説しました FinanceKitにより ユーザーのデバイス上の財務データに アプリからアクセスできます Transaction Picker APIとクエリの APIにはそれぞれ固有のプロセスと 要件があり 幅広いユースケースに対応できます Apple Developer Forumsでは いつでも質問を投稿し アドバイスを得ることができます 最後に ご意見がありましたら ぜひお聞かせください フィードバックアシスタントから 送信できます ご紹介した内容が みなさんのお役に立てば幸いです ご視聴ありがとうございました またお会いしましょう
-
-
5:38 - Check if financial data is available
// Check if financial data is available import FinanceKit let available = FinanceStore.isDataAvailable( .financialData ) guard available else { // No meaningful action can be performed return }
-
8:08 - Present the transaction picker
// Present the transaction picker 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") } } }
-
12:16 - Requesting authorization for financial data
// Requesting authorization for financial data import FinanceKit let store = FinanceStore.shared guard store.isDataAvailable(for: .financialData) else { // No meaningful action can be performed return } let authStatus = await store.requestAuthorization() guard authStatus == .authorized else { // User did not grant access to financial data, stop here return }
-
15:24 - Simple query to retrieve all Apple accounts
// Simple query to retrieve all Apple accounts 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)
-
18:12 - Get latest 7 available balances for account
// Get latest 7 available balances for account 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() }
-
20:27 - Retrieve all the transaction history for an account
// Retrieve all the transaction history for an account 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) }
-
21:04 - Use the history token to resume queries
// Use the history token to resume queries 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) }
-
21:41 - Non monitoring resumable queries
import FinanceKit let store = FinanceStore.shared let account: Account = ... let currentToken = loadToken() let transactionSequence = store.transactionHistory( forAccountID: account.id, since: currentToken, isMonitoring: false ) for try await change in transactionSequence { processChanges(change.inserted, change.updated, change.deleted) persist(token: change.newToken) }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。