Skip to content

Album Aggregation Cache

log5r edited this page Oct 7, 2026 · 2 revisions

アルバム集計キャッシュと回帰検証

2026-10-07、Issue #24 を修正した。比較元は c335eb2。未解決の P1 のうち、アルバム詳細の各行から全ライブラリの集計が繰り返されることをコードで確認でき、大きなライブラリの画面操作へ直接影響するため選択した。

原因と設計

AlbumBrowserView.albums は参照ごとに LibraryAlbum.grouped(items) を呼び、選択したアルバムの検索と行のアーティスト表示からも参照されていた。詳細の各行を描画するたび、全曲のグループ化・曲順ソート・アルバム順ソートが MainActor 上で繰り返される。

LibraryAlbumProjection が集計結果を保持し、表示はその配列を参照する。詳細で一度解決したアーティスト名を各行へ渡し、再生リスト名には再生する曲の displayAlbum を使う。集計と文字列比較の規則自体は変更していない。

入力配列の同一性は UUID だけでなくモデルの参照と順序で判定する。取り込み・削除・検索条件の変更は入力配列の更新で集計し直す。集計内のモデル読み取りを withObservationTracking で監視し、同じモデルのアルバム名・アーティスト・アルバムアーティスト・曲名・ディスク番号・曲番号・年・ジャンル・アートワーク ID のうち結果に影響する値の編集にも追従する。行自身が読む再生状態・選択状態・再生時間では集計をやり直さない。

Observation の通知は setter 完了前なので、再集計は次の MainActor のターンで行う。監視世代を照合し、入力切り替え・画面を閉じる操作後に残った通知を破棄する。監視用の寿命トークンを更新して以前の one-shot 監視を解除する。ワーカーにモデルを渡す処理は追加していない。

再現と検証

AlbumBrowserAggregationTests は 3000 曲・100 アルバムの実際の AlbumBrowserView を macOS の NSHostingView へ載せ、30 曲入りのアルバム詳細を表示する。高さ 1800 pt のウィンドウを用い、グリッドと詳細の往復、5 曲の行選択・再生項目変更、一括選択、アルバムアーティスト編集を行う。集計関数に渡すカウンターは測定用で、製品コードに計測カウンターを追加していない。

修正前の検証では比較元のビュー全体を一時的に戻し、albums の getter だけを同じ LibraryAlbum.grouped を呼ぶ測定用クロージャへ接続する。新しいキャッシュによる集計は使わず、同じウィンドウと操作で回数を数える。最終コードへ復元した後、同じ回帰テストを再実行する。

修正前は初回表示後 92 回、グリッド・詳細の往復後 154 回、5 曲の選択・再生項目変更と一括選択後 340 回だった。修正後は同じ各段階で 1 回を維持し、最後のメタデータ編集後にだけ 2 回になった。修正前は集計回数を 1 回とする回帰アサーションが失敗し、最終コードでは成功した。これはテスト全体の実測呼び出し回数であり、単一フレームあたりの回数や処理時間ではない。

LibraryAlbumProjectionTests は集計対象の各フィールドを個別に変更するテスト、アートワークと付加情報の削除、参照を置き換えない編集、入力追加・除去・空のフィルタ結果・復元、同じ UUID の別モデルへの置き換え、更新通知が保留中のクリアと再表示を確認する。

初回とメタデータ変更時の集計は同期処理として MainActor に残る。この検証は再集計回数と更新内容を確認するもので、実機 iPhone/iPad の描画時間、Instruments によるフレーム時間、音切れや電池消費の測定ではない。

macOS は Xcode 27.0/Swift 6 でビルドし、関連する Swift Testing の 47 tests・5 suites と実ビューを表示する XCTest 1 件が成功した。対象は LibraryAlbumTests、LibraryAlbumProjectionTests、LibraryBrowsingStateTests、LibraryBrowsingGroupInvalidationTests、LibraryListProjectionTests、AlbumBrowserAggregationTests。

iOS/iPadOS 向けの共通ソースは Xcode 27.1 Beta/iOS Simulator 27.1 SDK でビルド成功。今回の表示キャッシュは macOS と展開された iPad の AlbumBrowserView に適用される。iPhone のコンパクト表示は別の IPhoneTrackListView を使い、変更していない。LibraryBrowsingGroup.playbackQueue の呼び出し元も確認した。この経路は対象グループの曲順を構成するもので、今回の詳細行から全ライブラリを再集計する経路とは別に残る。

変更した 5 ファイルの strict lint は診断 0 件。全体 lint は既存の 43 警告・エラーなし。ベースラインの変更はない。

iPad の検索テスト補助処理の修正

初回と単独再実行では、検索・一括選択の UI テストが Hide keyboard の hittable 待ちで失敗した。修正前のアルバム表示へ戻しても同じ箇所で失敗した。単独再実行の xcresult の診断では、ウィンドウの高さが 1032 pt、閉じたキーボードのプレビューに属するボタンの y 座標が 1371 pt だった。アクセシビリティ階層に存在していても、画面外なので操作できない。

そこで ExpandedGroupFilteringUITests.dismissKeyboard は、ボタンのフレームがアプリの画面と交差する場合にだけ操作可能になるのを待ち、タップするようにした。キーボードを閉じた後の表示領域が元の高さへ戻ることを確認する既存のアサーションは維持した。アプリのキーボード操作は変更していない。

最終の iPad Pro 13-inch (M5) Simulator(iOS 27.0)で、詳細フィルタと検索・一括選択の UI テスト 2 件が成功した。先の実行ではアルバム単体テスト 14 tests・2 suites も成功している。終了済みの Xcode 実行の清掃が次の UI 実行を中断した試行は成功結果へ含めず、シミュレータ再起動後の単独実行で確認した。

確認条件 検証方法と結果
3000 曲・30 曲の詳細で、選択・再生項目変更・グリッド往復による再集計を避ける 実ビューの AlbumBrowserAggregationTests。修正前 92/154/340 回、修正後は各段階 1 回。編集後だけ 2 回
集計に使うメタデータの編集と削除に追従する LibraryAlbumProjectionTests の 9 フィールド個別変更、曲順・アーティスト表示・年/ジャンル・アートワーク削除の検証
取り込み・削除・検索結果変更で対象の曲が更新される 入力追加・除去・空結果・復元の単体テスト、iPad の詳細フィルタと検索 UI テスト
保存したアルバム画面と一括選択を検索結果に左右されず保持する ExpandedGroupFilteringUITests のアルバム検索・詳細フィルタ 2 ケースと関連する閲覧状態テスト
古い入力やクリア前の通知がキャッシュを再作成しない メタデータ通知を保留して同じ UUID の別参照へ切り替えるテスト、クリアと再表示の検証
両プラットフォームでビルドでき、新しい lint 違反を追加しない macOS/iOS Simulator ビルド、関連単体テスト、変更 5 ファイルの strict lint 0 件、全体 43 警告・エラーなし

修正コミットは b3aedae、UI テスト補助処理の補正は e992355。回帰テストはリポジトリに保存した。

iPad の最終 UI 実行では 2 ケースとも成功ログを確認したが、その後の Xcode の結果収集が停止したためプロセスを終了した。この実行の xcresult 最終化と xcodebuild の正常終了は未確認。ケースの成功ログは .build/issue24-ipad-ui-verified.log に保持した。macOS の関連単体テスト実行と iOS Simulator のビルドは正常終了している。

Clone this wiki locally