# 機能設計書 61-タブ一覧

## 概要

本ドキュメントは、Wikipedia iOSアプリにおける「タブ一覧」機能の設計仕様を記載する。開いている記事タブの一覧表示と管理機能を提供するものである。

### 本機能の処理概要

本機能は、ユーザーが開いている複数の記事タブを一覧表示し、タブ間の切り替え、新規タブの追加、タブの削除などを可能にする機能である。ブラウザのタブ機能に類似した操作性を提供し、複数記事の並行閲覧を効率的にサポートする。

**業務上の目的・背景**：ユーザーが複数のWikipedia記事を同時に参照・比較したい場合に、効率的なナビゲーションを提供する。研究やリサーチ、記事間の関連性の確認など、複数記事を行き来する必要がある場面で生産性を向上させる。

**機能の利用シーン**：
- 複数の関連記事を調査中に、開いている記事の一覧を確認したい場合
- 不要になったタブを閉じてメモリを解放したい場合
- 新しいタブを追加して別の記事を開きたい場合
- 以前開いた記事に素早く戻りたい場合

**主要な処理内容**：
1. 開いているタブの一覧をグリッド形式で表示する
2. タブをタップして該当記事に遷移する
3. タブの閉じるボタンでタブを削除する
4. 「+」ボタンで新規タブ（メインページ）を追加する
5. 全タブ閉じる機能を提供する
6. 記事サジェスト表示の切り替えを提供する
7. タブの共有機能を提供する

**関連システム・外部連携**：
- WMFArticleTabsDataController（WMFData）によるタブ情報の永続化
- ArticleSummaryControllerによる記事サマリー情報の取得

**権限による制御**：認証不要で利用可能。ログインユーザーとゲストユーザーで機能差はない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | Explore画面 | 遷移元画面 | タブボタンタップでタブ一覧画面へ遷移 |
| 2 | Places画面 | 遷移元画面 | タブボタンタップでタブ一覧画面へ遷移 |
| 3 | Saved画面 | 遷移元画面 | タブボタンタップでタブ一覧画面へ遷移 |
| 4 | Search画面 | 遷移元画面 | タブボタンタップでタブ一覧画面へ遷移 |
| 5 | Article画面 | 遷移元画面 | タブボタンタップでタブ一覧画面へ遷移 |

## 機能種別

画面表示 / ナビゲーション / CRUD操作（タブのCreate/Read/Delete）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| navigationController | UINavigationController | Yes | ナビゲーションコントローラー | nil不可 |
| theme | Theme | Yes | 現在のテーマ設定 | nil不可 |
| dataStore | MWKDataStore | Yes | データストア | nil不可 |

### 入力データソース

- WMFArticleTabsDataController.shared: タブ情報の取得元
- UserDefaults: サーベイ表示設定、記事サジェスト表示設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| タブ一覧 | [WMFArticleTab] | 開いているタブの配列 |
| タブ数 | Int | 現在開いているタブの総数 |
| サジェスト表示状態 | Bool | 記事サジェストの表示/非表示状態 |

### 出力先

- 画面表示: タブ一覧グリッドビュー（SwiftUI: WMFArticleTabsView）
- ナビゲーションバー: タブ数表示

## 処理フロー

### 処理シーケンス

```
1. TabsOverviewCoordinatorの初期化
   └─ navigationController、theme、dataStoreを受け取る
   └─ WMFArticleTabsDataController.sharedを取得

2. start()メソッド呼び出し
   └─ presentTabs()を実行

3. presentTabs()の実行
   └─ タブ数を非同期で取得（tabsCount()）
   └─ ローカライズ文字列を準備
   └─ WMFArticleTabsViewModelを構築
   └─ WMFArticleTabsViewを生成
   └─ HostingControllerでラップ
   └─ モーダル表示（overFullScreen）
   └─ サーベイ表示条件を確認し表示

4. ユーザー操作の処理
   └─ タブタップ: tappedTab() → 記事画面へ遷移
   └─ 追加ボタン: tappedAddTab() → メインページで新規タブ
   └─ 完了ボタン: tappedDone() → モーダルを閉じる
   └─ 共有ボタン: tappedShareTab() → 共有シート表示
   └─ 全削除: closeAllTabs確認後実行
```

### フローチャート

```mermaid
flowchart TD
    A[タブボタンタップ] --> B[TabsOverviewCoordinator.start]
    B --> C[presentTabs]
    C --> D[タブ数取得]
    D --> E[ViewModel構築]
    E --> F[タブ一覧表示]
    F --> G{ユーザー操作}

    G -->|タブ選択| H[tappedTab]
    H --> I[記事画面へ遷移]
    I --> J[モーダル閉じる]

    G -->|追加| K[tappedAddTab]
    K --> L[メインページで新規タブ]
    L --> J

    G -->|完了| M[tappedDone]
    M --> J

    G -->|共有| N[tappedShareTab]
    N --> O[共有シート表示]

    G -->|全削除| P[削除確認ダイアログ]
    P -->|Yes| Q[全タブ削除]
    Q --> F
    P -->|No| F
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-61-01 | 新規タブの初期ページ | 新規タブ作成時はメインページを表示する | 追加ボタンタップ時 |
| BR-61-02 | 同一タブ表示時の動作 | 現在表示中のタブをタップした場合はモーダルを閉じるのみ | タブ選択時に現在タブと一致 |
| BR-61-03 | サーベイ表示条件 | サーベイ表示条件を満たす場合のみ表示 | タブ一覧表示完了時 |
| BR-61-04 | 空タブ状態の表示 | タブが0件の場合は空状態メッセージを表示 | タブ数が0の場合 |

### 計算ロジック

タブ数の表示フォーマット:
```
navBarTitleFormat: "{{PLURAL:%1$d|%1$d tab|%1$d tabs}}"
```
複数形対応のローカライズ処理を適用する。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| タブ一覧取得 | ArticleTabs（Core Data） | SELECT | 全タブ情報の取得 |
| タブ削除 | ArticleTabs（Core Data） | DELETE | 指定タブの削除 |
| 新規タブ作成 | ArticleTabs（Core Data） | INSERT | 新規タブの追加 |
| サーベイカウント更新 | UserDefaults | UPDATE | サーベイ表示回数の更新 |

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

#### ArticleTabs

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | identifier, articles, lastModified | 全件取得 | 降順（最新順）でソート |
| DELETE | identifier | 指定タブIDで削除 | CASCADE削除 |
| INSERT | identifier, articles | 新規タブ情報 | メインページ記事を含む |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| E-61-01 | データ取得エラー | タブ情報の取得失敗 | 空配列で表示、ログ出力 |
| E-61-02 | 記事URL不正 | タブ内記事のURL解決失敗 | 該当タブをスキップ |
| E-61-03 | 言語設定不正 | appLanguageがnil | デフォルト言語を使用 |

### リトライ仕様

タブ情報取得失敗時はリトライなし。画面を閉じて再度開くことで再取得。

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

タブ削除操作はCore Dataコンテキスト内で実行され、即座にコミットされる。全タブ削除は一括トランザクションで実行。

## パフォーマンス要件

- タブ一覧表示: 500ms以内
- タブ選択から記事表示開始: 300ms以内
- 大量タブ（100件以上）でもスクロール60fps維持

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

- タブ情報はローカルに保存され、外部送信されない
- 記事URLはサニタイズ処理後に使用
- サーベイ送信時はユーザー識別情報を含まない

## 備考

- SwiftUI（WMFComponents）とUIKitのハイブリッド実装
- モーダル表示スタイルは`.overFullScreen`を使用
- iPadではポップオーバー表示に対応

---

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

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

### 推奨読解順序

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

タブ機能で扱うデータ構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | WMFArticleTabsDataController.swift | `WMFData/Sources/WMFData/Data Controllers/Article Tabs/WMFArticleTabsDataController.swift` | WMFArticleTab構造体とタブ操作メソッドを理解する |
| 1-2 | WMFArticleTabsViewModel.swift | `WMFComponents/Sources/WMFComponents/Components/Article Tabs/WMFArticleTabsViewModel.swift` | ViewModelのプロパティとローカライズ文字列構造を確認する |

**読解のコツ**: WMFArticleTabsDataControllerはシングルトンパターン（.shared）で実装されており、アプリ全体でタブ状態を共有している。

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

タブ一覧表示の起点となるCoordinatorを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | TabsOverviewCoordinator.swift | `Wikipedia/Code/TabsOverviewCoordinator.swift` | start()メソッドからpresentTabs()の流れを追う |

**主要処理フロー**:
1. **15-18行目**: start()メソッドでpresentTabs()を呼び出す
2. **21-28行目**: 初期化時にdataController、summaryControllerを設定
3. **93-189行目**: presentTabs()でSwiftUIビューを構築しモーダル表示
4. **262-286行目**: tappedTab()でタブ選択時の記事遷移処理

#### Step 3: View層を理解する

SwiftUIで実装されたタブ一覧ビューを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | WMFArticleTabsView.swift | `WMFComponents/Sources/WMFComponents/Components/Article Tabs/WMFArticleTabsView.swift` | タブグリッド表示のレイアウトを理解する |
| 3-2 | WMFArticleTabsHostingController.swift | `WMFComponents/Sources/WMFComponents/Components/Article Tabs/WMFArticleTabsHostingController.swift` | SwiftUIとUIKitのブリッジ処理を確認する |

#### Step 4: アナリティクスを理解する

操作ログ記録の仕組みを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | ArticleTabsFunnel.swift | `Wikipedia/Code/ArticleTabsFunnel.swift` | タブ操作のログイベントを確認する |

**主要処理フロー**:
- **339-378行目**: WMFArticleTabsLoggingDelegateプロトコル実装でログ記録

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

```
TabsCoordinatorManager.presentTabsOverview()
    │
    └─ TabsOverviewCoordinator.start()
           │
           └─ presentTabs()
                  │
                  ├─ WMFArticleTabsDataController.tabsCount()
                  │
                  ├─ WMFArticleTabsViewModel (構築)
                  │      └─ LocalizedStrings (設定)
                  │
                  ├─ WMFArticleTabsView (構築)
                  │
                  ├─ WMFArticleTabsHostingController (ラップ)
                  │
                  └─ WMFComponentNavigationController (表示)
                         │
                         └─ ユーザー操作コールバック
                                │
                                ├─ didTapTab → tappedTab()
                                │      └─ ArticleCoordinator.start()
                                │
                                ├─ didTapAddTab → tappedAddTab()
                                │      └─ ArticleCoordinator.start()
                                │
                                ├─ didTapDone → tappedDone()
                                │      └─ dismiss()
                                │
                                └─ didTapShareTab → tappedShareTab()
                                       └─ UIActivityViewController
```

### データフロー図

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

タブボタンタップ ───▶ TabsOverviewCoordinator ───▶ モーダル表示
                            │
                            ▼
                    WMFArticleTabsDataController
                            │
                            ▼
                    Core Data (ArticleTabs)
                            │
                            ▼
                    [WMFArticleTab] ───▶ WMFArticleTabsViewModel
                                              │
                                              ▼
                                        WMFArticleTabsView ───▶ タブグリッドUI
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| TabsOverviewCoordinator.swift | `Wikipedia/Code/TabsOverviewCoordinator.swift` | ソース | タブ一覧機能のCoordinator |
| WMFArticleTabsDataController.swift | `WMFData/Sources/WMFData/Data Controllers/Article Tabs/WMFArticleTabsDataController.swift` | ソース | タブデータの永続化と管理 |
| WMFArticleTabsView.swift | `WMFComponents/Sources/WMFComponents/Components/Article Tabs/WMFArticleTabsView.swift` | ソース | タブ一覧のSwiftUIビュー |
| WMFArticleTabsViewModel.swift | `WMFComponents/Sources/WMFComponents/Components/Article Tabs/WMFArticleTabsViewModel.swift` | ソース | タブ一覧のViewModel |
| WMFArticleTabsHostingController.swift | `WMFComponents/Sources/WMFComponents/Components/Article Tabs/WMFArticleTabsHostingController.swift` | ソース | SwiftUI/UIKitブリッジ |
| ArticleTabsFunnel.swift | `Wikipedia/Code/ArticleTabsFunnel.swift` | ソース | アナリティクスログ記録 |
| ArticleCoordinator.swift | `Wikipedia/Code/ArticleCoordinator.swift` | ソース | 記事画面への遷移処理 |
