# 機能設計書 10-ニュース表示

## 概要

本ドキュメントは、Wikipedia iOSアプリにおけるニュース表示機能の設計仕様を記述する。探索フィードの「In the news」カードから遷移し、最新のニュースストーリーと関連記事を表示する。

### 本機能の処理概要

**業務上の目的・背景**：世界で起きている最新のニュースとWikipediaコンテンツを結びつけ、ユーザーに時事問題への理解を深める機会を提供する。ニュースストーリーに関連するWikipedia記事へのアクセスを容易にする。

**機能の利用シーン**：
- 探索フィードの「In the news」カードをタップした時
- 「In the news」カードの「もっと見る」をタップした時
- ニュースストーリー内の関連記事をタップした時

**主要な処理内容**：
1. Wikipedia Feed APIから取得したニュースストーリーの表示
2. 各ストーリーの日付をヘッダーとして表示
3. 関連記事を横スクロールで表示
4. 記事タップ時の画面遷移
5. コンテキストメニューによる記事プレビュー

**関連システム・外部連携**：
- Wikipedia Feed API（In the news コンテンツ）
- Core Data（WMFFeedNewsStory）

**権限による制御**：
- 特別な権限は不要

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 10 | News画面 | 主画面 | ニュースストーリー一覧の表示 |
| 1 | Explore画面 | 呼び出し元 | フィードカードからの遷移 |
| 5 | Article画面 | 遷移先 | 関連記事への遷移 |

## 機能種別

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

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| stories | [WMFFeedNewsStory] | Yes | ニュースストーリー配列 | 空配列不可 |
| dataStore | MWKDataStore | Yes | データストアインスタンス | nil不可 |
| contentGroup | WMFContentGroup | No | フィードコンテンツグループ | 任意 |
| theme | Theme | Yes | 適用するテーマ | 有効なThemeオブジェクト |

### WMFFeedNewsStoryの構造

| プロパティ | 型 | 説明 |
|-----------|-----|------|
| storyHTML | String? | ストーリーのHTML形式テキスト |
| featuredArticlePreview | WMFFeedArticlePreview? | 主要記事のプレビュー |
| articlePreviews | [WMFFeedArticlePreview]? | 関連記事プレビュー配列 |
| midnightUTCMonthAndDay | Date? | ストーリーの日付（月日のみ、年は無効） |

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ニュースリスト | UICollectionView | ニュースストーリーのリスト表示 |
| ヘッダー | NewsCollectionViewHeader | 日付ヘッダー |
| セル | NewsCollectionViewCell | ストーリー内容と関連記事 |

### 出力先

- UICollectionView（メインコンテンツ）
- ナビゲーションバー（タイトル: "In the news"）

## 処理フロー

### 処理シーケンス

```
1. NewsViewController初期化
   └─ stories、dataStore、contentGroup、themeを受け取り
   └─ hidesBottomBarWhenPushed = true

2. viewDidLoad
   └─ NewsCollectionViewCellを登録
   └─ NewsCollectionViewHeaderを登録
   └─ collectionView.allowsSelection = false

3. viewWillAppear
   └─ configureNavigationBar()でナビバー設定
   └─ タイトル: CommonStrings.inTheNewsTitle

4. セクション表示
   └─ numberOfSections = stories.count + 1（section 0はダミー）
   └─ section > 0でニュースセルを表示

5. ヘッダー表示
   └─ headerTitle(for:)で日付文字列を生成
   └─ combineStoryDateWithCurrentYear()で年を補完
   └─ headerDateFormatterで"EEEEMMMMd"形式に整形

6. セル選択
   └─ SideScrollingCollectionViewCellDelegateで記事タップを検出
   └─ ArticleCoordinatorで記事画面に遷移
```

### フローチャート

```mermaid
flowchart TD
    A[フィードカードタップ] --> B[NewsViewController初期化]
    B --> C[viewDidLoad]
    C --> D[セル/ヘッダー登録]
    D --> E[viewWillAppear]
    E --> F[ナビバー設定]
    F --> G[ニュースリスト表示]
    G --> H{ユーザーアクション}
    H -->|スクロール| I[追加ストーリー表示]
    H -->|記事タップ| J[ArticleCoordinator.start]
    H -->|長押し| K[コンテキストメニュー表示]
    J --> L[Article画面]
    K --> M[ArticlePeekPreviewViewController]
    M --> N{コミット?}
    N -->|Yes| L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | セクション構成 | section 0はダミー、section 1以降がストーリー | 常時 |
| BR-002 | 日付フォーマット | "EEEEMMMMd"形式（曜日+月+日） | 常時 |
| BR-003 | 年の補完 | midnightUTCMonthAndDayの年は無効なため現在年-1で補完 | 常時 |
| BR-004 | 選択無効 | collectionView.allowsSelection = false | 常時 |
| BR-005 | ボトムバー非表示 | hidesBottomBarWhenPushed = true | 常時 |

### 計算ロジック

- セクション数: `stories.count + 1`
- ストーリーインデックス: `section - 1`（section > 0の場合）
- セル高さ推定: 350pt（画像高さ170pt + テキスト）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ストーリー取得 | WMFFeedNewsStory | SELECT | ニュースストーリーの取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 空ストーリー | storiesが空配列 | フィード画面に戻る |
| - | 初期化失敗 | NSCoder経由の初期化 | fatalError |

### リトライ仕様

- フィード画面でのPull-to-Refreshで更新

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

- 読み取り専用、トランザクション処理なし

## パフォーマンス要件

- 画面表示：1秒以内
- スクロール：60fps維持
- セル高さ推定：350pt（placeholderCellで事前計算）

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

- HTMLコンテンツはNSAttributedStringに変換して表示
- 外部URLは直接開かずArticleViewControllerを介して表示

## 備考

- 日付の年は無効値のため、現在年-1を使用して補完
- コンテキストメニューのアクションは現在無効化（UIの制約問題のため）
- EventLoggingでfeed.newsカテゴリとしてイベント記録

---

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

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

### 推奨読解順序

#### Step 1: メインViewControllerを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | NewsViewController.swift | `Wikipedia/Code/NewsViewController.swift` | ニュース画面のメインVC |

**主要コード**:
- **5-7行目**: クラス定義、cellReuseIdentifier、headerReuseIdentifier
- **9-14行目**: プロパティ（stories, dataStore, cellImageViewHeight, contentGroup）
- **19-27行目**: init - 初期化処理、hidesBottomBarWhenPushed設定
- **33-38行目**: viewDidLoad - セル/ヘッダー登録、allowsSelection = false
- **45-48行目**: configureNavigationBar - タイトル設定
- **106-112行目**: numberOfSections/numberOfItemsInSection
- **114-126行目**: cellForItemAt - セル構成
- **163-170行目**: headerDateFormatter - 日付フォーマット（EEEEMMMMd）
- **172-177行目**: story(for:) - section-1でストーリー取得
- **179-196行目**: combineStoryDateWithCurrentYear - 年の補完処理
- **198-206行目**: headerTitle(for:) - ヘッダータイトル生成

#### Step 2: セルコンポーネントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | NewsCollectionViewCell.swift | `Wikipedia/Code/NewsCollectionViewCell.swift` | ニュースセル |
| 2-2 | SideScrollingCollectionViewCell.swift | `Wikipedia/Code/SideScrollingCollectionViewCell.swift` | 横スクロール基底クラス |

**主要コード（NewsCollectionViewCell）**:
- **3行目**: SideScrollingCollectionViewCellを継承
- **15-17行目**: styles - HTMLスタイル定義
- **19-25行目**: updateDescriptionHTMLStyle - HTML→NSAttributedString変換
- **27-31行目**: descriptionHTML - HTMLテキストプロパティ

#### Step 3: 記事選択とコンテキストメニューを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | NewsViewController.swift | `Wikipedia/Code/NewsViewController.swift` | SideScrollingCollectionViewCellDelegate |

**主要コード**:
- **210-220行目**: sideScrollingCollectionViewCell:didSelectArticleWithURL - 記事選択時の処理
- **234-254行目**: contextMenu - コンテキストメニュー生成
- **256-270行目**: willCommitPreview - プレビューからの遷移

#### Step 4: モデルを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | WMFFeedNewsStory.h | `Wikipedia/Code/WMFFeedNewsStory.h` | ニュースストーリーモデル |

**主要コード**:
- **9行目**: storyHTML - ストーリーのHTMLテキスト
- **11行目**: featuredArticlePreview - 主要記事プレビュー
- **13行目**: articlePreviews - 関連記事配列
- **15行目**: midnightUTCMonthAndDay - 日付（年は無効）

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

```
NewsViewController
    │
    ├─ UICollectionView
    │      ├─ NewsCollectionViewCell (section > 0)
    │      │      ├─ descriptionLabel (HTML→NSAttributedString)
    │      │      └─ SideScrollingCollectionView（関連記事）
    │      │
    │      └─ NewsCollectionViewHeader (日付表示)
    │             └─ headerDateFormatter（EEEEMMMMd形式）
    │
    ├─ SideScrollingCollectionViewCellDelegate
    │      └─ sideScrollingCollectionViewCell:didSelectArticleWithURL
    │             └─ ArticleCoordinator.start()
    │
    ├─ NestedCollectionViewContextMenuDelegate
    │      ├─ contextMenu() -> UIContextMenuConfiguration
    │      │      └─ ArticlePeekPreviewViewController
    │      └─ willCommitPreview()
    │             └─ ArticleCoordinator.start()
    │
    └─ MEPEventsProviding
           └─ eventLoggingCategory: .feed
           └─ eventLoggingLabel: .news
```

### データフロー図

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

WMFFeedNewsStory[] ────▶ NewsViewController
    │                          │
    ├─ storyHTML ─────────▶    │ (descriptionLabel)
    ├─ articlePreviews ───▶    │ (横スクロールセル)
    └─ midnightUTCMonthAndDay ▶│
                               │
                               ▼
                    combineStoryDateWithCurrentYear()
                               │
                               ▼
                    headerDateFormatter.string()
                               │
                               ▼
                         UICollectionView
                               │
                               ▼
                    [ニュースストーリーリスト表示]
                               │
         ┌─────────────────────┼─────────────────────┐
         ▼                     ▼                     ▼
 [ヘッダー:日付]      [セル:ストーリー]    [横スクロール:記事]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| NewsViewController.swift | `Wikipedia/Code/` | ソース | メインVC |
| NewsCollectionViewCell.swift | `Wikipedia/Code/` | ソース | ニュースセル |
| NewsCollectionViewHeader.swift | `Wikipedia/Code/` | ソース | 日付ヘッダー |
| NewsCollectionViewHeader.xib | `Wikipedia/Code/` | リソース | ヘッダーレイアウト |
| NewsExploreCollectionViewCell.swift | `Wikipedia/Code/` | ソース | フィード用セル |
| SideScrollingCollectionViewCell.swift | `Wikipedia/Code/` | ソース | 横スクロール基底 |
| WMFFeedNewsStory.h | `Wikipedia/Code/` | ソース | ストーリーモデル |
| WMFFeedNewsStory.m | `Wikipedia/Code/` | ソース | モデル実装 |
| WMFFeedNewsStory+LocalizedStrings.swift | `Wikipedia/Code/` | ソース | ローカライズ拡張 |
| WMFFeedArticlePreview | `WMF Framework/` | ソース | 記事プレビューモデル |
| ArticleCoordinator.swift | `Wikipedia/Code/` | ソース | 記事画面遷移 |
| ArticlePeekPreviewViewController.swift | `Wikipedia/Code/` | ソース | 記事プレビュー |
