シリーズ:iOS版『見開きPDFビューア』開発記録
この記事は、ChatGPTと壁打ちしながら作成しました。また、開発時の作業ログと、Gitの変更履歴・ソースコードを調査した結果を含みます。
第5回では、端末上の動作を確認する診断機能と、不具合の原因を探るためのログを紹介しました。今回は、Codexへコードの評価を依頼し、その結果から改善計画を立て、段階的に実施した記録です。
Viewの分割と画像キャッシュ、ドキュメント更新のルール化、iPhone Duoを想定した表示ルールの見直し、GitHub Actionsの導入を取り上げます。コードの基準は、10月20日時点で調査したtestingブランチの91f0853です。
コードの評価を頼み、改善計画を実行する
10月14日5時43分(日本時間)、Codexにコードから技術レベルを推測するよう依頼しました。以降のプロンプトは全文、回答は該当部分の抜粋です。途中の省略は「(中略)」で示します。
筆者のプロンプト(全文)
最新のコードから開発者の技術レベルを推測してください。
Codexの回答(抜粋)
MainViewにファイル取込、コピー、パスワード処理、状態復元、テスト用PDF生成、画面構成が集中しており、責務がかなり大きいです。(中略)
新しい
@Observable/Swift Testingと、ObservableObject/Combine/XCTestが混在しています。移行途中としては自然ですが、設計方針の統一余地があります。
これはCodexによる当時の評価であり、筆者の技術力を客観的に証明するものではありません。実際の改善には、役割の集中や状態管理の混在といった指摘を手がかりにしました。
続いて6時7分、評価結果を改善計画につなげました。
筆者のプロンプト(全文)
この結果を元に、コードを改善します。ブランチtestingを対象にコードを改善する計画を立ててください。
Codexの回答(抜粋)
実装を分割する前に、現在の正常動作をテストで固定します。
(中略)
SwiftUIのView分割では、単なるcomputed propertyへの移動ではなく、入力を限定した独立
View型を優先します。
Viewの分割は、この評価をきっかけに立てた改善計画を実行した結果です。以下では、実際の変更と、その後に追加された改善策を見ていきます。
画面と処理を役割ごとに切り出す
10月14日には、画面や処理を役割ごとに分ける変更が続いています。MainViewから読書画面を、SettingsViewから補助画面を切り出し、その後も操作部やヘルプ、画像生成などの整理を進めました。

図2 コードの評価から改善計画、修正とテストへ進む流れと、Viewを役割ごとに分ける考え方。
| 切り出した対象 | 本稿の対象コードでの主なファイル |
|---|---|
| ページの組み合わせ | PDFPageGrouping.swift |
| PDF画像生成・キャッシュ | PDFPageImageRenderer.swift |
| 読書画面の操作部 | PDFReaderControls.swift |
| 設定画面の各セクション | SettingsSections.swift |
| ヘルプ内容 | HelpContent.swift |
第3回で紹介した、testdata/のL2R・R2LのPDFから設定を読み取るテストに加え、ページの組み合わせ自体を確認するテストもあります。PDFPageGroupingでは、PDFKitのオブジェクトを使わずにページ番号の配列を計算できるため、期待する並びになるかを確認しやすくなりました。画像生成やヘルプの内容も独立したファイルに移り、画面の構築と、それ以外の処理を分ける構成になっています。
キャッシュは、描画負荷の改善提案から入った
最初からキャッシュの導入を指定していたわけではありません。10月14日7時25分、残る改善点を尋ねると、7時26分の回答に描画負荷の改善案が含まれていました。
筆者のプロンプト(全文)
ブランチtestingの改善すべき点の残りはありますか?
Codexの回答(抜粋)
改善候補:
- ページ画像を表示サイズ・倍率・画質設定単位でキャッシュ
CIContextを再利用- 検索ハイライト更新だけではPDF画像を再生成しない構造へ分離
7時27分には、次のように計画を依頼しました。
筆者のプロンプト(全文)
その順に修正する計画を立ててください。
Codexの回答(抜粋)
完了条件: 検索結果更新だけではPDFが再描画されず、既存表示品質を維持すること。
その後の同日7時30分のコミットでキャッシュが追加されています。

図3 同じ条件のページ画像があれば再利用し、なければ生成して保存するキャッシュの模式図。
画像キャッシュは NSCache を使い、ページの同一性、表示サイズ、画面倍率、高画質設定、シャープネス量をキーに含めています。設定が異なる画像を同一の結果として再利用しないためです。
本稿の対象コードでは、件数上限24、コスト上限128MiBを設定し、概算の画像コストをピクセル幅×高さ×4で計算しています。描画の最長辺にも通常2048、高画質4096ピクセルという上限があります。128MiBという値は、キャッシュに設定した目安です。この値が適切かどうかまでは、まだ詳しく検討していません。
ドキュメント更新を、依頼からルールへ変える
機能変更に合わせ、ヘルプ、README、公開サイトを最新の仕様へ更新する依頼も繰り返していました。更新の指示を忘れがちだったため、途中からルールとして残すようにしました。
10月15日10時42分には、次のように依頼しました。
筆者のプロンプト(全文)
ビルド番号を更新する時に、ヘルプ画面、LICENSE、README、docs/*.htmlを最新の仕様に合わせて修正するルールを追加してください。
Codexの回答(抜粋)
変更が不要なファイルも確認自体は必須とし、確認完了までビルド番号更新作業を完了扱いにしない旨も明記しました。
同日のコミットでAGENTS.mdが追加されています。ビルドのたびにREADMEを自動生成するスクリプトではなく、Codexへの作業ルールとして更新忘れを防ぐ方法にしました。

図4 ビルド番号の更新にドキュメントの確認・更新を結び付ける作業ルール。
iPhone Duo対応を機に、縦長なら単ページという前提を見直す
当初はiPhoneやiPadで読むことを想定し、開発の途中で縦長の画面では単ページ表示をデフォルトにしました。10月12日の作業ログには「端末の種類に関係なく、画面が縦長の場合はデフォルトで単一ページ表示に変更してください」という依頼があり、同日の変更では設定の初期値とREADMEなどの説明を更新しています。保存済みの設定がある場合は、その値を使う実装です。
その後、iPhone Duoへの対応を考えたことで、この前提を見直しました。10月14日7時16分の依頼は、入力ミスも含めてそのまま引用しています。
筆者のプロンプト(全文)
iPhone Duo対応(最適化?0の計画を立ててください。
Codexの回答(抜粋)
レイアウト判定をモデル化
- 現在の
isPortraitだけによる判定を廃止(中略)
- 端末名や固定画面サイズによるDuo判定は行わない
- 現在表示中のPDFページをレイアウト変更後も維持する
ちなみに、プロンプト中の'0'は打ち間違い(typo)ですが、Codexは筆者の意図通りに回答しています。同じようなことは一連の作業中に何度もありました。
この計画のうち、Gitとコードで確認できたのは、表示領域とSize Classに応じた判定への変更です。機種固有の対応すべてが完了したという意味ではありません。
同日7時24分のコミットでは、表示サイズと横幅のSize ClassをまとめるPDFViewportLayoutが追加されています。単ページを優先する判定は、次のように変わりました。
isPortrait && horizontalSizeClass != .regular
縦長で、かつ横幅がregularではない場合に、縦向き時の単ページ設定を適用します。縦長でもregular幅なら、見開き表示の設定を有効にしたまま2ページを表示できます。これはiPhone Duoという機種名を調べて分岐する処理ではなく、画面から渡された表示領域の特性を使う実装です。同じ条件になるiPadなどにも影響します。
PDFPageGroupingTestsには、縦長・regular幅で見開きを使えることを確認するテストが追加されています。サイズやSize Classの変更時に、表示していたページを維持する処理も変更されています。

図5 iPhone、iPad、開いたiPhone Duoの画面の形を比較した模式図。
改善案だったCIを、後から導入する
CIは、コードの品質や改善点を分析してもらったときの提案に含まれていて、少し後に導入した、というのが筆者の記憶です。10月17日20時17分には実行状況を質問しました。testingブランチでも検証を動かしてほしい、という意図を込めた依頼でした。
筆者のプロンプト(全文)
ブランチtestingで、GitHub Actionは実行されますか?
Codexの回答(抜粋)
現在のローカル設定では、
testingブランチへの push で GitHub Actions は実行されません。(中略)
※ GitHub 上の最新状態は未確認です。
筆者のプロンプト(全文)
ブランチmainと同様のワークフロー定義の設定を検討してください。改良が可能なら改良を検討してください。
Codexの回答(抜粋)
確認した結果、
mainで動いているのは GitHub Pages の公開処理だけでした。アプリのビルド・テスト用 CI はありません。(中略)
改良として、変更ファイルに応じた実行制御、古い検証の自動キャンセル、タイムアウト、失敗時のテスト結果保存を入れると、実行時間と調査の手間を減らせます。
最初の回答はローカル確認の範囲に限られていました。20時21分には別のセッションで構成を検討し、mainのPages公開処理とアプリ検証用CIの違いが整理されました。実施を指示した後、10月17日の変更で検証用ワークフローが追加されています。
CI(継続的インテグレーション)では、コードなどの変更に合わせて検証を自動実行します。今回の定義では、mainとtestingへのpushやプルリクエストで、対象ファイルに変更があった場合に検証を動かします。HTMLのローカル参照確認、共有Schemeでの単体テスト、手動実行で指定した場合の主要3画面のUIテストが設定されています。iOSのテスト結果とビルドログの保存期間は7日です。

図6 GitHub Actionsによるドキュメント検証とiOS検証の流れ。
10月17日20時57分の作業ログには、CIでInfo.plistを処理できず終了コード65になった実行結果を貼り付けた記録も残っています。その直後、必要なInfo.plistをGitの追跡対象に加える修正が行われています。ワークフローを追加して終わりではなく、実行先で不足していたファイルへの対応が続いていました。
GitHub Actionsでは、変更時のビルドやテストに加え、配布作業も自動化できます。GitHubの説明。今回は、テスターやストアへの配布を自動化するCDまでは導入していません。小規模な個人開発でも、push時に同じ手順で検証を動かせることには意味があります。「数カ月後の自分」が手順を忘れたときや、手元と異なる環境で問題が起きたときへの備えとして、後から効果を感じるかもしれません。
評価をきっかけに、改善を続ける仕組みを整える
今回の作業は、コードの評価を頼み、改善点を具体的な変更へつなげるところから始まりました。画面や処理を分けるだけでなく、表示ルールを見直し、ドキュメントの更新やCIによる検証も開発の流れに組み込んでいきました。
次の第7回では、サンプルPDFを開く機能と、公開ページに使うスクリーンショットや動画を準備した経緯を紹介します。
ソースコードと本稿の対象
ソースコードはGitHubのtyamada/MihirakiPDFViewerで公開しています。本稿は10月20日時点で調査したtestingブランチの91f0853を基準にしています。今回、アプリのビルドやテストの再実行、GitHub Actionsの実行結果の取得は行っていません。
前の記事:第5回:端末診断とログ
