# 機能設計書 1-記事表示

## 概要

本ドキュメントは、Wikipedia iOSアプリにおける記事表示機能の設計仕様を記述する。Wikipedia記事のWebViewによる表示、目次ナビゲーション、参照リンク処理を担当する中核機能である。

### 本機能の処理概要

**業務上の目的・背景**：Wikipedia iOSアプリの主要機能として、ユーザーがWikipedia記事を快適に閲覧できる環境を提供する。オフライン閲覧、テーマ切り替え、読み進め状態の保存など、ネイティブアプリならではの付加価値を実現し、Webブラウザよりも優れた読書体験を提供することを目的とする。

**機能の利用シーン**：検索結果から記事を選択した時、フィードから記事をタップした時、リンクから別記事へ遷移した時、保存した記事を開いた時など、アプリ内のあらゆる場所から記事を閲覧する際に使用される。アプリの最も頻繁に使用される機能である。

**主要な処理内容**：
1. 記事URLからMobile HTMLコンテンツを取得し、WKWebViewで表示
2. リードイメージ（メイン画像）の読み込みと表示
3. 目次（Table of Contents）の生成とナビゲーション
4. 記事内参照リンク・脚注のポップオーバー表示
5. スクロール位置の保存と復元（State Restoration）
6. テーマ（ライト/ダーク/セピア）の適用
7. フォントサイズの調整
8. 記事のオフラインキャッシュ管理
9. 共有機能との連携
10. 編集機能への遷移
11. ウォッチリスト追加・削除

**関連システム・外部連携**：
- Wikipedia Mobile Content Service API（記事HTMLの取得）
- Article Summary API（記事メタデータの取得）
- MediaWiki API（編集履歴、ウォッチリスト状態の取得）
- Core Data（閲覧履歴、保存状態の永続化）
- ArticleCacheController（オフラインキャッシュ）

**権限による制御**：
- 未ログインユーザー：記事閲覧、共有、保存が可能
- ログインユーザー：上記に加えウォッチリスト機能、編集機能が利用可能
- 編集権限：ユーザーグループにより編集可否が決定される

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | Article画面 | 主画面 | Wikipedia記事のWebViewによる表示、目次ナビゲーション、参照リンク処理 |
| 77 | ColumnarCollectionViewController画面 | 補助画面 | リスト表示画面の共通レイアウト基盤 |
| 78 | ArticleCollectionViewController画面 | 補助画面 | 記事コレクション表示の共通基盤 |
| 81 | ArticleURLList画面 | 参照画面 | URL指定記事リスト表示 |
| 82 | ArticleFetchedResults画面 | 参照画面 | CoreDataからの記事表示 |

## 機能種別

表示処理 / データ取得 / 状態管理 / ナビゲーション

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| articleURL | URL | Yes | 表示する記事のURL | Wikipedia URLであること |
| dataStore | MWKDataStore | Yes | データストアインスタンス | nil不可 |
| theme | Theme | Yes | 適用するテーマ | 有効なThemeオブジェクト |
| source | ArticleSource | Yes | 記事の参照元 | enum値 |
| schemeHandler | SchemeHandler | No | カスタムURLスキームハンドラー | nil許容 |
| previousPageViewObjectID | NSManagedObjectID | No | 前のページビューのオブジェクトID | nil許容 |

### 入力データソース

- Wikipedia Mobile Content Service API
- ローカルキャッシュ（ArticleCacheController）
- Core Data（WMFArticleエンティティ）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 記事HTML | String | WebViewに表示されるHTML |
| leadImage | UIImage | 記事のリード画像 |
| tableOfContentsItems | [TableOfContentsItem] | 目次項目の配列 |
| viewedScrollPosition | Double | 閲覧位置（スクロール位置） |
| viewedFragment | String | 閲覧中のセクションアンカー |

### 出力先

- WKWebView（記事本文の表示）
- UIImageView（リード画像の表示）
- Core Data（閲覧履歴、スクロール位置の永続化）
- Analytics（ページビューログ）

## 処理フロー

### 処理シーケンス

```
1. ArticleViewController初期化
   └─ articleURL、dataStore、themeを受け取り初期化
   └─ WMFArticleオブジェクトをfetchOrCreate

2. viewDidLoad
   └─ WebView、ツールバー、目次コントローラーのセットアップ
   └─ 通知ハンドラーの登録

3. viewWillAppear
   └─ 目次コントローラーのセットアップ
   └─ ツールバー更新
   └─ loadIfNecessaryの呼び出し

4. load（記事読み込み）
   └─ state = .loading
   └─ Page Content Service JSインターフェースのセットアップ
   └─ Mobile HTMLリクエストの実行
   └─ WebViewへのロード

5. 記事ロード完了
   └─ フッターのセットアップ
   └─ スクロール位置の復元
   └─ ページビューのログ記録
   └─ state = .loaded

6. viewWillDisappear
   └─ スクロール位置の保存
   └─ タイマーの停止
```

### フローチャート

```mermaid
flowchart TD
    A[ArticleViewController初期化] --> B{WMFArticle取得成功?}
    B -->|No| C[初期化失敗 return nil]
    B -->|Yes| D[viewDidLoad]
    D --> E[WebView/Toolbar/TOCセットアップ]
    E --> F[viewWillAppear]
    F --> G{state == .initial?}
    G -->|Yes| H[load開始]
    G -->|No| I[既にロード済み]
    H --> J[JSインターフェースセットアップ]
    J --> K[Mobile HTMLリクエスト]
    K --> L{キャッシュあり?}
    L -->|Yes| M[キャッシュから表示]
    L -->|No| N[APIからフェッチ]
    M --> O[WebViewロード]
    N --> O
    O --> P[ロード完了処理]
    P --> Q[フッターセットアップ]
    Q --> R[スクロール位置復元]
    R --> S[state = .loaded]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 閲覧履歴記録 | 記事表示時に閲覧履歴に追加 | 記事ロード成功時 |
| BR-002 | 有意義閲覧判定 | 30秒以上閲覧で「有意義に閲覧」とマーク | タイマー経過時 |
| BR-003 | スクロール位置保存 | 画面離脱時に現在のスクロール位置を保存 | viewWillDisappear時 |
| BR-004 | 状態復元 | アプリ再開時に前回のスクロール位置を復元 | isRestoringState = true時 |
| BR-005 | キャッシュ優先 | 状態復元時はキャッシュを優先的に使用 | isRestoringState = true時 |

### 計算ロジック

- スクロール位置の計算：`webView.scrollView.contentOffset.y`
- 垂直オフセットパーセンテージ：`scrollView.verticalOffsetPercentage`

## データベース操作仕様

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 記事取得 | WMFArticle | SELECT/INSERT | 記事メタデータの取得または作成 |
| 閲覧履歴追加 | ReadHistory | INSERT | 閲覧履歴レコードの追加 |
| 閲覧位置保存 | WMFArticle | UPDATE | viewedScrollPosition, viewedFragmentの更新 |
| 有意義閲覧フラグ | WMFArticle | UPDATE | wasSignificantlyViewedの更新 |

### テーブル別操作詳細

#### WMFArticle

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | key | articleURL.wmf_inMemoryKey | 記事の一意キー |
| UPDATE | viewedScrollPosition | Double(webView.scrollView.contentOffset.y) | スクロール位置 |
| UPDATE | viewedFragment | anchor | 閲覧中セクション |
| UPDATE | wasSignificantlyViewed | true | 30秒以上閲覧時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ネットワークエラー | APIリクエスト失敗 | エラーメッセージ表示、オフライン保存記事なら表示 |
| - | パース エラー | HTML解析失敗 | エラー画面表示 |
| - | WebView終了 | WebContentプロセス終了 | WebViewをリロード |

### リトライ仕様

- WebContentプロセス終了時：自動的にWebViewをリロード
- ネットワークエラー時：Pull-to-Refreshで手動リトライ

## トランザクション仕様

- Core Data操作は`viewContext`で実行
- スクロール位置保存時は`managedObjectContext.save()`で即時コミット

## パフォーマンス要件

- 記事表示までの目標時間：3秒以内（キャッシュ時は1秒以内）
- スクロールは60fpsを維持
- メモリ使用量：WebView含め200MB以下を目標

## セキュリティ考慮事項

- カスタムURLスキーム（app://）を使用しセキュアなコンテンツ配信
- JavaScript実行はPage Content Serviceの公式スクリプトのみ許可
- ユーザー認証情報はWMFAuthenticationManagerで一元管理

## 備考

- iOS 16.4以降ではデバッグビルドでWebInspectorが有効
- iPad対応でトレイトコレクション変更時のレイアウト調整あり

---

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

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

### 推奨読解順序

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

まず、プログラム間で受け渡されるデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | WMFArticle | `WMF Framework/` | 記事メタデータを保持するCore Dataエンティティ |
| 1-2 | TableOfContentsItem.swift | `Wikipedia/Code/TableOfContentsItem.swift` | 目次項目のデータ構造 |
| 1-3 | MediaList | `WMF Framework/` | 記事内メディア情報 |

**読解のコツ**: WMFArticleはCore Dataのマネージドオブジェクトで、記事のメタデータ（タイトル、説明、保存状態等）と閲覧状態（スクロール位置、閲覧フラグメント）を管理する。

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

処理の起点となるファイル・関数を特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ArticleViewController.swift | `Wikipedia/Code/ArticleViewController.swift` | メインのViewControllerクラス |

**主要処理フロー**:
1. **180-203行目**: `init` - articleURL、dataStoreを受け取り初期化、WMFArticleをfetchOrCreate
2. **452-458行目**: `viewDidLoad` - setup()を呼び出しWebView等のセットアップ
3. **576-590行目**: `load` - 記事読み込みの開始、JSインターフェースセットアップ後にloadPage呼び出し
4. **722-742行目**: `loadPage` - Mobile HTMLリクエストを作成しWebViewにロード

#### Step 3: WebView制御を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ArticleWebMessagingController | `Wikipedia/Code/` | JavaScript-Swift間のメッセージング |
| 3-2 | SchemeHandler | `Wikipedia/Code/` | カスタムURLスキームの処理 |

**主要処理フロー**:
- **1396-1413行目**: `setupPageContentServiceJavaScriptInterface` - JSブリッジのセットアップ

#### Step 4: 目次機能を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ArticleViewController+TableOfContents.swift | `Wikipedia/Code/ArticleViewController+TableOfContents.swift` | 目次表示の拡張 |
| 4-2 | TableOfContentsViewController.swift | `Wikipedia/Code/TableOfContentsViewController.swift` | 目次のViewController |
| 4-3 | ArticleTableOfContentsDisplayController.swift | `Wikipedia/Code/ArticleTableOfContentsDisplayController.swift` | 目次表示制御 |

**主要処理フロー**:
- **4-17行目（+TableOfContents）**: `getVisibleSection` - 現在表示中のセクションを取得
- **27-33行目（+TableOfContents）**: `showTableOfContents` - 目次を表示

#### Step 5: 参照・脚注表示を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | ArticleViewController+References.swift | `Wikipedia/Code/ArticleViewController+References.swift` | 参照表示の拡張 |
| 5-2 | WMFReferencePageViewController.swift | `Wikipedia/Code/WMFReferencePageViewController.swift` | 参照ポップオーバーのページViewController |

#### Step 6: 共有機能を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 6-1 | ArticleViewController+Sharing.swift | `Wikipedia/Code/ArticleViewController+Sharing.swift` | 共有機能の拡張 |
| 6-2 | ShareActivityController.swift | `Wikipedia/Code/ShareActivityController.swift` | 共有アクティビティの制御 |

**主要処理フロー**:
- **4-13行目（+Sharing）**: `shareArticle` - 選択テキストを取得し共有ダイアログ表示

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

```
ArticleViewController
    │
    ├─ ArticleToolbarController (ツールバー制御)
    │
    ├─ ArticleTableOfContentsDisplayController
    │      └─ TableOfContentsViewController (目次表示)
    │
    ├─ ArticleWebMessagingController (JS-Swiftブリッジ)
    │
    ├─ SchemeHandler (URLスキーム処理)
    │
    ├─ ArticleFetcher (記事データ取得)
    │      └─ Session (HTTPリクエスト)
    │
    ├─ ArticleCacheController (キャッシュ管理)
    │
    ├─ WatchlistController (ウォッチリスト)
    │
    └─ ReadingThemesControlsArticlePresenter (テーマ制御)
```

### データフロー図

```
[入力]                    [処理]                         [出力]

articleURL ─────────────▶ ArticleViewController ──────▶ WKWebView表示
                               │
dataStore ──────────────▶      │ (WMFArticle取得)
                               │
Mobile Content Service ──▶     │ (HTML取得) ──────────▶ 記事HTML表示
                               │
                               │ (目次生成) ──────────▶ TableOfContentsItems
                               │
                               │ (状態保存) ──────────▶ Core Data
                                                         (viewedScrollPosition)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ArticleViewController.swift | `Wikipedia/Code/` | ソース | メインViewController |
| ArticleViewController+TableOfContents.swift | `Wikipedia/Code/` | ソース | 目次機能拡張 |
| ArticleViewController+References.swift | `Wikipedia/Code/` | ソース | 参照表示拡張 |
| ArticleViewController+Sharing.swift | `Wikipedia/Code/` | ソース | 共有機能拡張 |
| ArticleViewController+Editing.swift | `Wikipedia/Code/` | ソース | 編集機能拡張 |
| ArticleViewController+Watchlist.swift | `Wikipedia/Code/` | ソース | ウォッチリスト拡張 |
| ArticleViewController+ArticleToolbarHandling.swift | `Wikipedia/Code/` | ソース | ツールバー操作拡張 |
| ArticleViewController+ArticleWebMessageHandling.swift | `Wikipedia/Code/` | ソース | WebMessageハンドリング拡張 |
| ArticleToolbarController.swift | `Wikipedia/Code/` | ソース | ツールバー制御 |
| ArticleTableOfContentsDisplayController.swift | `Wikipedia/Code/` | ソース | 目次表示制御 |
| TableOfContentsViewController.swift | `Wikipedia/Code/` | ソース | 目次ViewController |
| ArticleWebMessagingController.swift | `Wikipedia/Code/` | ソース | JS-Swiftメッセージング |
| SchemeHandler.swift | `Wikipedia/Code/` | ソース | カスタムURLスキーム |
| ArticleFetcher.swift | `Wikipedia/Code/` | ソース | 記事データ取得 |
| ArticleCacheController.swift | `Wikipedia/Code/` | ソース | キャッシュ管理 |
| WMFArticle | `WMF Framework/` | データモデル | 記事エンティティ |
