# 画面設計書 82-ArticleFetchedResults画面

## 概要

本ドキュメントは、Wikipedia iOSアプリにおけるArticleFetchedResults画面の設計仕様を定義するものである。この画面は、CoreDataのNSFetchedResultsControllerを使用して記事を動的に取得・表示するための基底クラスである。

### 本画面の処理概要

ArticleFetchedResults画面は、NSFetchedResultsControllerを活用してCoreDataから記事データをリアルタイムに取得し、コレクションビューに表示する抽象基底クラスである。データベースの変更を自動的に監視し、UIを効率的に更新する機能を提供する。

**業務上の目的・背景**：閲覧履歴や検索結果など、CoreDataに保存された記事データを動的に表示する必要がある画面は多数存在する。これらの画面で共通となるNSFetchedResultsController連携とUICollectionView更新ロジックを一元化し、コードの重複を防ぎ、保守性を向上させる目的で設計されている。サブクラスは具体的なフェッチ条件を定義するだけで、データバインディングの複雑さから解放される。

**画面へのアクセス方法**：この画面は抽象基底クラスであり、直接インスタンス化されることはない。サブクラス（例：閲覧履歴画面など）を通じて利用される。

**主要な操作・処理内容**：
1. NSFetchedResultsControllerのセットアップと初期フェッチ実行
2. CoreDataの変更監視とUICollectionViewへの自動反映
3. 記事アイテムの表示と選択処理
4. スワイプによる削除アクション
5. 一括削除機能（全件削除）

**画面遷移**：
- 遷移元：サブクラス固有（例：Saved画面、設定画面など）
- 遷移先：Article画面（記事詳細）、ArticlePeekPreview画面（プレビュー）

**権限による表示制御**：基底クラスでは特になし。サブクラスで必要に応じて実装。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | 記事表示 | 主機能 | CoreDataからの記事表示 |
| 14 | 記事保存 | 補助機能 | 保存済み記事の管理 |

## 画面種別

一覧（基底クラス）

## URL/ルーティング

iOSネイティブアプリのため、URLルーティングなし。抽象基底クラスとして、サブクラスで具体的なルーティングが定義される。

## 入出力項目

| 項目名 | 型 | 入力/出力 | 必須 | 説明 |
|--------|------|---------|------|------|
| dataStore | MWKDataStore | 入力 | 必須 | データストアへの参照（didSet時にFRCセットアップ） |
| fetchedResultsController | NSFetchedResultsController<WMFArticle> | 内部 | 必須 | CoreDataフェッチ結果コントローラー |
| collectionViewUpdater | CollectionViewUpdater<WMFArticle> | 内部 | 必須 | コレクションビュー更新管理 |

## 表示項目

| 項目名 | 説明 | 表示形式 |
|--------|------|----------|
| 記事タイトル | 記事の表示タイトル | テキスト |
| 記事説明 | 記事の概要説明 | テキスト（1行） |
| 記事サムネイル | 記事のサムネイル画像 | 画像（右寄せ） |
| セパレーター | セル間の区切り線 | 水平線 |

## イベント仕様

### 1-dataStore設定時

dataStoreプロパティが設定された際の初期化処理。

1. `didSet`オブザーバーが呼び出される
2. `setupFetchedResultsController(with:)`が呼び出される（サブクラス実装）
3. `CollectionViewUpdater`が初期化され、delegateが設定される

### 2-初回表示時のフェッチ

画面の初回表示時にデータをフェッチする処理。

1. `viewWillHaveFirstAppearance(_:)`が呼び出される
2. `collectionViewUpdater.performFetch()`でNSFetchedResultsControllerのフェッチを実行
3. コレクションビューがリロードされる

### 3-記事削除

スワイプまたは一括操作で記事を削除する処理。

1. `delete(at:)`または`deleteAll()`が呼び出される
2. `article.removeFromReadHistory()`で閲覧履歴から削除
3. NSFetchedResultsControllerDelegateを通じてUIが自動更新

### 4-全件削除ボタン押下

ナビゲーションバーの削除ボタン押下時の処理。

1. `deleteButtonPressed(_:)`が呼び出される
2. UIAlertControllerで確認ダイアログを表示
3. ユーザー確認後、`deleteAll()`を実行（サブクラス実装）

### 5-データ変更通知

CoreDataの変更がUIに反映される処理。

1. NSFetchedResultsControllerDelegateのコールバックが呼び出される
2. CollectionViewUpdaterが変更を処理
3. `collectionViewUpdater(_:didUpdate:)`で可視セルを再設定
4. 空状態の更新チェック

### 6-セルハイライト制御

スワイプ中のセルハイライトを制御する処理。

1. `collectionView(_:shouldHighlightItemAt:)`が呼び出される
2. editControllerのスワイプ状態をチェック
3. スワイプ中（translation != 0）の場合はハイライトを無効化

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 記事削除 | WMFArticle | UPDATE | 閲覧履歴からの削除（isInReadingHistoryなど） |
| データフェッチ | WMFArticle | SELECT | fetchedResultsControllerによる取得 |
| 一括削除 | WMFArticle | UPDATE | 全件の閲覧履歴削除（サブクラス実装） |

### テーブル別更新項目詳細

#### WMFArticle

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | readHistory関連 | removeFromReadHistory()で更新 | サブクラス実装に依存 |
| SELECT | - | fetchRequest.predicateで条件指定 | サブクラスで定義 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|----------|
| deleteAllConfirmationText | 確認 | （サブクラスで定義） | 全件削除確認時 |
| deleteAllText | アクション | （サブクラスで定義） | 削除アクションボタン |
| deleteAllCancelText | キャンセル | （サブクラスで定義） | キャンセルボタン |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| setupFetchedResultsController未実装 | アサーション失敗（サブクラス実装必須） |
| 無効なインデックスパス | isValidIndexPath確認後、nilを返却 |
| 削除処理エラー | showError(error)でエラー表示 |

## 備考

- 抽象基底クラスであり、`setupFetchedResultsController(with:)`はサブクラスで必ず実装する必要がある
- CollectionViewUpdaterと連携してNSFetchedResultsControllerの変更をUIに効率的に反映
- `canDelete(at:)`はデフォルトでtrueを返し、すべてのアイテムが削除可能
- 画面非表示時にeditControllerをクローズして状態をリセット

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

NSFetchedResultsControllerとCollectionViewUpdaterの関係を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | WMFArticle | `WMF/Sources/WMF/DataStore/WMFArticle+*.swift` | 記事データモデル、removeFromReadHistory()メソッド |
| 1-2 | CollectionViewUpdater.swift | `Wikipedia/Code/CollectionViewUpdater.swift` | FRCデリゲートとコレクションビュー更新の橋渡し |

**読解のコツ**: CollectionViewUpdaterはジェネリクスを使用しており、NSFetchedResultsControllerの型パラメータに応じて動作する。

#### Step 2: エントリーポイントを理解する

dataStore設定時の初期化フローを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ArticleFetchedResultsViewController.swift | `Wikipedia/Code/ArticleFetchedResultsViewController.swift` | dataStoreのdidSetオブザーバー |

**主要処理フロー**:
1. **13-19行目**: `dataStore` didSet - FRCセットアップとCollectionViewUpdater初期化
2. **9-11行目**: `setupFetchedResultsController(with:)` - サブクラス実装必須のメソッド
3. **86-89行目**: `viewWillHaveFirstAppearance(_:)` - performFetch実行

#### Step 3: データ取得と表示を理解する

FRCからのデータ取得とセル表示の流れを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ArticleFetchedResultsViewController.swift | `Wikipedia/Code/ArticleFetchedResultsViewController.swift` | article(at:)、UICollectionViewDataSource |

**主要処理フロー**:
- **21-26行目**: `article(at:)` - インデックスパスから記事取得
- **28-30行目**: `articleURL(at:)` - 記事からURL取得
- **100-113行目**: `numberOfSections`, `numberOfItemsInSection` - データソース実装

#### Step 4: 更新とデリゲートを理解する

データ変更時のUI更新フローを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | CollectionViewUpdater.swift | `Wikipedia/Code/CollectionViewUpdater.swift` | NSFetchedResultsControllerDelegate実装 |
| 4-2 | ArticleFetchedResultsViewController.swift | `Wikipedia/Code/ArticleFetchedResultsViewController.swift` | CollectionViewUpdaterDelegate実装 |

**主要処理フロー**:
- **67-75行目**: `collectionViewUpdater(_:didUpdate:)` - 可視セルの再設定とempty状態更新

### プログラム呼び出し階層図

```
ArticleFetchedResultsViewController
    │
    ├─ dataStore (didSet)
    │      ├─ setupFetchedResultsController(with:) [サブクラス実装]
    │      │      └─ NSFetchedResultsController<WMFArticle>生成
    │      └─ CollectionViewUpdater初期化
    │             └─ delegate設定
    │
    ├─ viewWillHaveFirstAppearance(_:)
    │      └─ collectionViewUpdater.performFetch()
    │             └─ fetchedResultsController.performFetch()
    │
    ├─ article(at:)
    │      └─ fetchedResultsController.object(at:)
    │
    ├─ delete(at:)
    │      └─ article.removeFromReadHistory()
    │
    └─ CollectionViewUpdaterDelegate
           └─ collectionViewUpdater(_:didUpdate:)
                  ├─ configure(cell:) for可視セル
                  └─ updateEmptyState()
```

### データフロー図

```
[CoreData]                 [FRC/Updater]              [UI]

WMFArticle ◀───────── NSFetchedResultsController ────▶ UICollectionView
   │                           │                           │
   │ (変更)                    │ (デリゲート)               │ (更新)
   ▼                           ▼                           ▼
removeFromReadHistory ──▶ CollectionViewUpdater ─────▶ reloadData/
                                                        batchUpdates
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ArticleFetchedResultsViewController.swift | `Wikipedia/Code/ArticleFetchedResultsViewController.swift` | ソース | 基底ViewController |
| ArticleCollectionViewController.swift | `Wikipedia/Code/ArticleCollectionViewController.swift` | ソース | 記事コレクション基底クラス |
| CollectionViewUpdater.swift | `Wikipedia/Code/CollectionViewUpdater.swift` | ソース | FRC→UICollectionView橋渡し |
| ColumnarCollectionViewController.swift | `Wikipedia/Code/ColumnarCollectionViewController.swift` | ソース | カラムレイアウト基底クラス |
| WMFArticle | `WMF/Sources/WMF/DataStore/` | ソース | 記事データモデル |
| MWKDataStore.swift | `WMF/Sources/WMF/DataStore/MWKDataStore.swift` | ソース | データストア |
