# 機能設計書 58-画像キャッシュ

## 概要

本ドキュメントは、Wikipedia iOSアプリにおける画像キャッシュ機能の設計を記述する。この機能は、記事内の画像やサムネイルを効率的にキャッシュし、高速表示とオフラインアクセスを可能にする。

### 本機能の処理概要

**業務上の目的・背景**：Wikipedia記事には多数の画像が含まれており、これらを効率的にキャッシュすることでユーザー体験を向上させる。メモリキャッシュとファイルキャッシュの2層構造により、高速表示とオフライン閲覧の両方を実現する。

**機能の利用シーン**：
- 記事閲覧時の画像表示
- サムネイル画像の事前読み込み（プリフェッチ）
- 保存記事のオフライン画像表示
- GIFアニメーション対応

**主要な処理内容**：
1. URL をキーとした画像取得（fetchImage）
2. メモリキャッシュの確認と利用
3. ファイルキャッシュからの読み込み
4. ネットワークからのダウンロードと保存
5. 複数リクエストの結合（completion coalescing）
6. GIF/静止画の判別と適切な処理

**関連システム・外部連携**：
- CacheController（基底クラス）
- ImageFetcher（画像取得）
- FLAnimatedImage（GIFアニメーション）
- Core Graphics（画像処理）

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | Article画面 | 利用画面 | 記事内画像表示 |
| 7 | Gallery画面 | 利用画面 | ギャラリー画像表示 |
| 1 | Explore画面 | 利用画面 | カードサムネイル表示 |

## 機能種別

キャッシュ管理 / 画像処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| url | URL | Yes | 画像URL | 有効なURL、nilチェック |
| priority | Float | No | ダウンロード優先度 | 0.0-1.0 |

### 入力データソース

Wikimediaサーバーの画像URL

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Image | Image | 静止画像とアニメーション画像を含むラッパー |
| ImageDownload | ImageDownload | 画像とメタデータを含む結果 |

### 出力先

- メモリキャッシュ（NSCache）
- ファイルキャッシュ（Permanent Cache/ディレクトリ）

## 処理フロー

### 処理シーケンス

```
1. 画像取得リクエスト
   └─ fetchImage(withURL:priority:...)
2. メモリキャッシュ確認
   └─ memoryCachedImage(withURL:)
3a. メモリキャッシュヒット
   └─ 即座にsuccess返却（origin: .memory）
3b. メモリキャッシュミス
   └─ fetchData(withURL:...)
4. Completion結合確認
   └─ dataCompletionManager.add()
5a. 既存リクエスト中
   └─ 完了待ち
5b. 新規リクエスト
   └─ imageFetcher.dataForURL()
6. データ取得成功
   └─ createImage() + addToMemoryCache()
7. 成功通知
   └─ success callback
```

### フローチャート

```mermaid
flowchart TD
    A[fetchImage] --> B{メモリキャッシュ?}
    B -->|Yes| C[即座に返却]
    B -->|No| D[fetchData]
    D --> E{既存リクエスト?}
    E -->|Yes| F[完了待ち]
    E -->|No| G[imageFetcher.dataForURL]
    G --> H{ファイルキャッシュ?}
    H -->|Yes| I[キャッシュから読込]
    H -->|No| J[ネットワークダウンロード]
    I --> K[createImage]
    J --> K
    K --> L{GIF?}
    L -->|Yes| M[FLAnimatedImage生成]
    L -->|No| N[UIImage生成]
    M --> O[メモリキャッシュ追加]
    N --> O
    O --> P[success callback]
    F --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-58-01 | メモリキャッシュ制限 | totalCostLimit = 10000000（ピクセル数） | 常時 |
| BR-58-02 | Completion結合 | 同一URLへの複数リクエストを結合 | 同時リクエスト時 |
| BR-58-03 | GIF判定 | MIMEType = "image/gif"でアニメーション処理 | GIF画像 |
| BR-58-04 | PDF対応 | PDFファイルは最初のページを画像化 | PDF取得時 |
| BR-58-05 | スキーム補完 | スキームなしURLにhttps://を付加 | URL処理時 |

### 計算ロジック

- メモリキャッシュコスト: width * height（ピクセル数）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 画像追加 | CacheItem | INSERT | 画像メタデータ追加 |
| ダウンロード完了 | CacheItem | UPDATE | isDownloaded=true |
| 画像削除 | CacheItem | DELETE | アイテム削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| dataNotFound | データなし | 画像データ取得失敗 | failure callback |
| invalidOrEmptyURL | 無効URL | URLがnilまたは空 | failure callback |
| invalidResponse | 無効レスポンス | 画像生成失敗 | failure callback |
| fileError | ファイルエラー | ファイル操作失敗 | failure callback |
| failureGeneratingUniqueKey | キー生成失敗 | UniqueKey生成失敗 | failure callback |

### リトライ仕様

なし（キャンセル時はエラーコールバックをスキップ）

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

Core Dataコンテキストで管理

## パフォーマンス要件

- メインスレッドアサーション（fetchImage）
- 優先度ベースのダウンロード
- キャンセル可能なフェッチ（トークンベース）

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

- HTTPS通信
- App Container内でのキャッシュ保存

## 備考

ImageはstaticImageとanimatedImageを保持するラッパー構造体で、GIFアニメーションと静止画の両方に対応する。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ImageCacheController.swift | `WMF Framework/ImageCacheController.swift` | Image, ImageDownload, TypedImageData |

**読解のコツ**:
- **49-52行目**: FetchResult構造体
- **66-76行目**: FetchErrorの種類
- **340-349行目**: TypedImageData（MIMEType付きデータ）

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ImageCacheController.swift | `WMF Framework/ImageCacheController.swift` | メインクラス |

**主要処理フロー**:
1. **139-165行目**: fetchImage(withURL:priority:...) - 画像取得エントリーポイント
2. **82-132行目**: fetchData(withURL:priority:...) - データ取得
3. **191-208行目**: cachedImage(withURL:) - キャッシュ確認
4. **222-229行目**: memoryCachedImage(withURL:) - メモリキャッシュ
5. **242-260行目**: createImage(data:mimeType:) - 画像生成

#### Step 3: キャッシュ管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ImageCacheController.swift | `WMF Framework/ImageCacheController.swift` | メモリキャッシュ |
| 3-2 | CacheController.swift | `WMF Framework/CacheController.swift` | ファイルキャッシュ（基底） |

**主要処理フロー**:
- **38行目**: memoryCache = NSCache<NSString, Image>()
- **43行目**: memoryCache.totalCostLimit = 10000000
- **57-64行目**: finishFileSave() - ファイル保存後のメモリキャッシュ追加
- **231-238行目**: addToMemoryCache() - メモリキャッシュへの追加

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

```
UIImageView.wmf_setImage() / 他の呼び出し元
    │
    └─ ImageCacheController
           ├─ fetchImage(withURL:priority:...)
           │      ├─ memoryCachedImage(withURL:)
           │      │      └─ memoryCache.object(forKey:)
           │      │
           │      └─ fetchData(withURL:priority:...)
           │             ├─ dataCompletionManager.add()
           │             └─ imageFetcher.dataForURL()
           │                    └─ URLSession.dataTask()
           │
           ├─ createImage(data:mimeType:)
           │      ├─ FLAnimatedImage (GIF)
           │      └─ CGImageSourceCreateWithData() (静止画)
           │
           └─ addToMemoryCache(image:url:)
                  └─ memoryCache.setObject()
```

### データフロー図

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

画像URL ───▶ fetchImage() ───▶ メモリキャッシュ確認
               │                     │
               │                     ▼
               │              [ヒット]───▶ Image返却
               │                     │
               │              [ミス]───▶ fetchData()
               │                           │
               ▼                           ▼
        ファイルキャッシュ ───▶ [ヒット]───▶ createImage()
               │                           │
        [ミス]───▶ ネットワーク ───▶ データ取得
                                          │
                                          ▼
                                   addToMemoryCache()
                                          │
                                          ▼
                                   Image返却
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ImageCacheController.swift | `WMF Framework/` | ソース | 画像キャッシュ管理 |
| CacheController.swift | `WMF Framework/` | ソース | 基底クラス |
| ImageFetcher.swift | `WMF Framework/` | ソース | 画像取得 |
| ImageControllerCompletionManager.swift | `WMF Framework/` | ソース | Completion結合 |
| UIImageView+WMFImageFetching.swift | `WMF Framework/` | ソース | UIImageView拡張 |
| FLAnimatedImage | 外部ライブラリ | ソース | GIFアニメーション |
