# 画面設計書 85-SavedProgress画面

## 概要

本ドキュメントは、Wikipedia iOSアプリにおけるSavedProgress画面の設計仕様を定義するものである。この画面は、記事のオフラインダウンロード進捗を表示するプログレスバーコンポーネントである。

### 本画面の処理概要

SavedProgress画面は、保存した記事のオフラインダウンロード進捗をUIProgressViewで視覚的に表示するコンポーネントである。ProgressContainerのarticleFetcherProgressをKVO（Key-Value Observing）で監視し、ダウンロード状況に応じて表示/非表示を自動的に切り替える。

**業務上の目的・背景**：ユーザーがリーディングリストに記事を保存すると、オフライン閲覧のために記事コンテンツがバックグラウンドでダウンロードされる。本画面は、このダウンロード処理の進捗をユーザーに視覚的にフィードバックし、処理が行われていることを明示する。特に多数の記事を一括保存した場合や、ネットワーク状況が悪い場合に、ユーザーが処理状況を把握できるようにする。

**画面へのアクセス方法**：Saved画面に子ViewControllerとして埋め込まれ、記事ダウンロードが進行中の場合に自動的に表示される。ダウンロードが完了または不要な場合は非表示になる。

**主要な操作・処理内容**：
1. NSProgressオブジェクトの監視開始と終了
2. ダウンロード進捗のリアルタイム表示
3. 進捗状況に応じたアニメーション付き表示/非表示切り替え
4. 最小表示時間の制御（短すぎる表示の防止）

**画面遷移**：
- 遷移元：なし（Saved画面の子コンポーネントとして埋め込み）
- 遷移先：なし

**権限による表示制御**：特になし。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 57 | 記事キャッシュ | 主機能 | オフラインダウンロード進捗表示 |
| 14 | 記事保存 | 関連機能 | 保存記事のダウンロード管理 |

## 画面種別

オーバーレイ（進捗バー）

## URL/ルーティング

iOSネイティブアプリのため、URLルーティングなし。Storyboard/XIBからインスタンス化されるか、コードで子ViewControllerとして追加される。

## 入出力項目

| 項目名 | 型 | 入力/出力 | 必須 | 説明 |
|--------|------|---------|------|------|
| theme | Theme | 入力 | 必須 | 現在のテーマ設定 |

## 表示項目

| 項目名 | 説明 | 表示形式 |
|--------|------|----------|
| 進捗ラベル | 「Article download in progress...」 | テキスト |
| プログレスバー | ダウンロード進捗を示すバー | UIProgressView |

## イベント仕様

### 1-画面表示時の進捗監視開始

viewWillAppear時に進捗オブジェクトの監視を開始する処理。

1. `viewWillAppear(_:)`が呼び出される
2. `beginProgressObservations()`が実行される
3. ProgressContainer.sharedのarticleFetcherProgressを監視開始
4. 新しいProgressオブジェクトが設定されるたびにprogressViewにバインド
5. Progress.isFinishedを監視して完了状態を検出

### 2-進捗オブジェクト更新時

ProgressContainerのarticleFetcherProgressが新しいオブジェクトに置き換わった際の処理。

1. KVOオブザーバーが変更を検出
2. progressView.observedProgressに新しいProgressオブジェクトを設定
3. 前のisFinished監視をinvalidate
4. 新しいProgressのisFinished監視を開始

### 3-進捗完了判定と表示制御

Progress.isFinishedの変更に基づく表示/非表示制御。

1. isFinished監視コールバックが呼び出される
2. `progress.wmf_shouldShowProgressUI()`で表示すべきか判定
3. 表示不要の場合は`animate(isHidden: true)`で非表示化
4. 表示必要の場合、`WMFMinProgressDurationBeforeShowingProgressUI`秒後に表示チェック
5. 最小時間経過後も進捗中なら`animate(isHidden: false)`で表示

### 4-画面非表示時の監視終了

viewWillDisappear時に進捗監視を終了する処理。

1. `viewWillDisappear(_:)`が呼び出される
2. `endProgressObservations()`が実行される
3. progressIsFinishedObservation?.invalidate()
4. progressObjectWasSetObservation?.invalidate()
5. progressView.observedProgress = nil

### 5-表示/非表示アニメーション

表示状態を切り替える際のアニメーション処理。

1. `animate(isHidden:)`が呼び出される
2. `UIView.transition`でcrossDissolveアニメーション（0.3秒）
3. view.isHiddenプロパティを更新

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

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

本画面では直接的なデータベース操作は行わない。進捗情報はProgressContainerを通じてメモリ上で管理される。

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|----------|
| saved-pages-progress-syncing | 情報 | Article download in progress... | 常時表示（ラベル固定テキスト） |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| Progressオブジェクトnil | progressView.observedProgressをnilに設定、UI更新なし |
| 短時間での完了 | WMFMinProgressDurationBeforeShowingProgressUI未満なら非表示のまま |

## 備考

- NSProgressは再利用できないため、新しい進捗が開始されるたびに監視を再設定する必要がある
- ProgressContainerはシングルトンで、アプリ全体でProgressオブジェクトを共有
- 最小表示時間制御により、一瞬だけ表示されてすぐ消えるというUX問題を防止
- Themeableプロトコルに準拠し、テーマ変更に対応

---

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

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

### 推奨読解順序

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

進捗管理に関連するクラスを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ProgressContainer.swift | `Wikipedia/Code/ProgressContainer.swift` | シングルトン、articleFetcherProgress |
| 1-2 | Foundation Progress | iOS標準 | NSProgress、isFinished、fractionCompleted |

**読解のコツ**: ProgressContainerはNSObjectを継承し、@objcMembersでKVO対応。dynamicプロパティでKVO監視可能。

#### Step 2: ViewControllerを理解する

SavedProgressViewControllerの基本構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | SavedProgressViewController.swift | `Wikipedia/Code/SavedProgressViewController.swift` | IBOutlet、ライフサイクル |

**主要処理フロー**:
1. **12-15行目**: `viewDidLoad()` - ラベルテキスト設定
2. **17-21行目**: `viewWillAppear` - 監視開始
3. **23-26行目**: `viewWillDisappear` - 監視終了

#### Step 3: 進捗監視ロジックを理解する

KVOによる進捗監視の実装を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | SavedProgressViewController.swift | `Wikipedia/Code/SavedProgressViewController.swift` | beginProgressObservations、endProgressObservations |

**主要処理フロー**:
- **28-55行目**: `beginProgressObservations()` - 二重KVO監視（Progress設定とisFinished）
- **30-31行目**: articleFetcherProgressの監視
- **37-53行目**: isFinishedの監視と最小表示時間制御
- **57-63行目**: `endProgressObservations()` - 監視解除

#### Step 4: 表示制御を理解する

表示/非表示アニメーションとテーマ対応を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | SavedProgressViewController.swift | `Wikipedia/Code/SavedProgressViewController.swift` | animate、apply(theme:) |

**主要処理フロー**:
- **65-69行目**: `animate(isHidden:)` - crossDissolveアニメーション
- **71-76行目**: `apply(theme:)` - 背景、テキスト、プログレス色の設定

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

```
SavedProgressViewController
    │
    ├─ viewDidLoad()
    │      └─ label.text設定（ローカライズ文字列）
    │
    ├─ viewWillAppear(_:)
    │      └─ beginProgressObservations()
    │             │
    │             ├─ observe(\ProgressContainer.articleFetcherProgress)
    │             │      ├─ progressView.observedProgress設定
    │             │      └─ observe(\Progress.isFinished)
    │             │             ├─ wmf_shouldShowProgressUI()判定
    │             │             ├─ exceededMinSyncInProgressDuration管理
    │             │             └─ animate(isHidden:)
    │             │
    │             └─ progressIsFinishedObservation設定
    │
    ├─ viewWillDisappear(_:)
    │      └─ endProgressObservations()
    │             ├─ progressIsFinishedObservation?.invalidate()
    │             ├─ progressObjectWasSetObservation?.invalidate()
    │             └─ progressView.observedProgress = nil
    │
    └─ apply(theme:)
           ├─ view.backgroundColor設定
           ├─ label.textColor設定
           └─ progressView.progressTintColor設定
```

### データフロー図

```
[ProgressContainer]        [KVO監視]              [UI]

articleFetcherProgress ──▶ observe()変更検出 ──▶ UIProgressView
    │                           │                    │
    │ (新Progress設定)          │ (observedProgress)  │ (自動更新)
    ▼                           ▼                    ▼
Progress.isFinished ────▶ observe()完了検出 ──▶ animate(isHidden:)
                                │
                    ┌───────────┴───────────┐
                    ▼                       ▼
        wmf_shouldShowProgressUI()     最小時間チェック
        false → 非表示                true → 表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| SavedProgressViewController.swift | `Wikipedia/Code/SavedProgressViewController.swift` | ソース | メインViewController |
| ProgressContainer.swift | `Wikipedia/Code/ProgressContainer.swift` | ソース | 進捗オブジェクト共有 |
| Progress+WMFExtensions | `WMF/Sources/WMF/` | ソース | wmf_shouldShowProgressUI()拡張 |
| SavedViewController.swift | `Wikipedia/Code/SavedViewController.swift` | ソース | 親画面（埋め込み先） |
