# 画面設計書 40-PushNotificationsSettings画面

## 概要

本ドキュメントは、Wikipedia iOSアプリにおけるPushNotificationsSettings画面の設計仕様を定義するものです。

### 本画面の処理概要

PushNotificationsSettings画面は、プッシュ通知の詳細設定を管理する画面です。NotificationSettings画面と似た機能を持ちますが、より詳細な説明テキストを含み、ナビゲーションバーの設定にも対応しています。システム通知権限の状態に応じて適切なUIを表示し、Echo通知へのサブスクリプション管理を行います。

**業務上の目的・背景**：
ユーザーがWikipediaのプッシュ通知を詳細に制御できるようにすることが目的です。通知の利点を説明しながら、有効化/無効化の操作を提供します。

**画面へのアクセス方法**：
Settings画面から「Push notifications」を選択して遷移します。

**主要な操作・処理内容**：
1. プッシュ通知の有効化/無効化スイッチ
2. システム設定への誘導（権限拒否時）
3. Echo通知へのサブスクリプション/アンサブスクリプション
4. デバイストークン取得のリトライ処理

**画面遷移**：
- 遷移元：Settings画面
- 遷移先：システム設定アプリ

**権限による表示制御**：
- システム通知権限許可済み：サブスクリプションスイッチ表示
- システム通知権限未決定：有効化スイッチ表示
- システム通知権限拒否：システム設定への外部リンク表示

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 45 | プッシュ通知設定 | 主機能 | 通知設定を行う |

## 画面種別

設定（テーブルビュー）

## URL/ルーティング

本画面は独立したURLを持たず、Settings画面からのpush遷移でアクセスされます。

## 入出力項目

| 項目名 | 入力/出力 | データ型 | 説明 |
|--------|----------|---------|------|
| authenticationManager | 入力 | WMFAuthenticationManager | 認証管理 |
| notificationsController | 入力 | WMFNotificationsController | 通知コントローラ |

## 表示項目

| 項目名 | データ型 | 説明 |
|--------|---------|------|
| tableView | UITableView | 設定一覧 |
| WMFSettingsTableViewCell | UITableViewCell | 設定セル |
| WMFTableHeaderFooterLabelView | UIView | セクションヘッダー（説明テキスト） |

## イベント仕様

### 1-プッシュ通知スイッチON

スイッチをONにすると、requestPushPermissions()が呼ばれ、システム通知権限を要求します。許可された場合はデバイストークンを取得し、Echo通知にサブスクライブします。

### 2-プッシュ通知スイッチOFF

スイッチをOFFにすると、unsubscribeFromEchoNotifications()が呼ばれ、Echo通知からアンサブスクライブします。

### 3-システム設定への遷移

権限拒否状態でセルをタップすると、UIApplication.wmf_openAppSpecificSystemSettings()でシステム設定に遷移します。

### 4-アプリがフォアグラウンドに戻った時

UIApplication.willEnterForegroundNotificationを監視し、フォアグラウンドに戻ったらセクションを更新します。認証状態が変わった場合は前の画面に戻ります。

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| サブスクライブ | UserDefaults | UPDATE | wmf_isSubscribedToEchoNotificationsをtrueに |
| アンサブスクライブ | UserDefaults | UPDATE | wmf_isSubscribedToEchoNotificationsをfalseに |
| サブスクライブ | - | API Call | Echo通知APIにサブスクライブ |
| アンサブスクライブ | - | API Call | Echo通知APIからアンサブスクライブ |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|---------|
| settings-notifications-header | 説明文 | "Be alerted to activity related to your account..." | セクションヘッダー |
| settings-notifications-echo-failure-title | エラー | "Unable to Check for Echo Notification subscriptions" | サブスクリプション失敗時 |
| settings-notifications-echo-failure-message | エラー | "An error occurred while checking for notification subscriptions..." | サブスクリプション失敗時 |

## 例外処理

| 例外条件 | 処理内容 |
|---------|---------|
| デバイストークン取得失敗 | RetryBlockTaskでリトライ、失敗時はアラート表示 |
| サブスクリプション失敗 | リトライアラート表示 |
| アンサブスクリプション失敗 | リトライアラート表示 |
| 認証状態変更（フォアグラウンド復帰時） | 前の画面に戻る |

## 備考

- SubSettingsViewControllerを継承
- WMFNavigationBarConfiguringプロトコルに準拠
- RetryBlockTaskでデバイストークン取得をリトライ
- スイッチの操作中はuserInteractionEnabledをfalseに設定
- UIApplication.willEnterForegroundNotificationを監視

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PushNotificationsSettingsItem | `Wikipedia/Code/PushNotificationsSettingsViewController.swift` | 設定項目プロトコル |
| 1-2 | PushNotificationsSettingsSection | `Wikipedia/Code/PushNotificationsSettingsViewController.swift` | セクション構造体 |
| 1-3 | PushNotificationsSettingsActionItem | `Wikipedia/Code/PushNotificationsSettingsViewController.swift` | アクション項目 |
| 1-4 | PushNotificationsSettingsSwitchItem | `Wikipedia/Code/PushNotificationsSettingsViewController.swift` | スイッチ項目 |

**読解のコツ**: PushNotificationsSettingsItem、ActionItem、SwitchItemの3種類のアイテムタイプがあります。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | PushNotificationsSettingsViewController.swift | `Wikipedia/Code/PushNotificationsSettingsViewController.swift` | init(authenticationManager:notificationsController:)で初期化 |

**主要処理フロー**:
1. **53-57行目**: init時にauthenticationManagerとnotificationsControllerを受け取り
2. **63-73行目**: viewDidLoadでセル登録、willEnterForeground監視
3. **82-93行目**: viewWillAppearでupdateSections()、ナビゲーションバー設定

#### Step 3: 状態別UI更新を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | PushNotificationsSettingsViewController.swift | `Wikipedia/Code/PushNotificationsSettingsViewController.swift` | updateSections()メソッド |

**主要処理フロー**:
- **108-159行目**: updateSections()でnotificationPermissionsStatusに基づきUI切り替え
- **116-127行目**: authorized時 - サブスクリプションスイッチ表示
- **132-144行目**: notDetermined時 - 有効化スイッチ表示
- **145-153行目**: denied時 - システム設定リンク表示

#### Step 4: サブスクリプション処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | PushNotificationsSettingsViewController.swift | `Wikipedia/Code/PushNotificationsSettingsViewController.swift` | requestPushPermissions, subscribe, unsubscribe |

**主要処理フロー**:
- **250-288行目**: requestPushPermissions() - 権限要求とトークン取得
- **290-297行目**: showDeviceTokenRetryAlert() - リトライアラート
- **299-315行目**: subscribeToEchoNotifications() - サブスクライブ処理
- **317-333行目**: unsubscribeFromEchoNotifications() - アンサブスクライブ処理

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

```
PushNotificationsSettingsViewController
    │
    ├─ init(authenticationManager:notificationsController:)
    │
    ├─ viewDidLoad()
    │      └─ NotificationCenter.addObserver(willEnterForeground)
    │
    ├─ viewWillAppear()
    │      ├─ updateSections()
    │      └─ configureNavigationBar()
    │
    ├─ updateSections()
    │      └─ notificationsController.notificationPermissionsStatus()
    │             ├─ .authorized/.provisional/.ephemeral
    │             │      └─ PushNotificationsSettingsSwitchItem (サブスクリプションスイッチ)
    │             ├─ .notDetermined
    │             │      └─ PushNotificationsSettingsSwitchItem (有効化スイッチ)
    │             └─ default (.denied)
    │                    └─ PushNotificationsSettingsActionItem (システム設定リンク)
    │
    ├─ userDidTapSwitch()
    │      ├─ isOn == true → requestPushPermissions()
    │      └─ isOn == false → unsubscribeFromEchoNotifications()
    │
    ├─ requestPushPermissions()
    │      ├─ RetryBlockTask (deviceToken待ち)
    │      ├─ notificationsController.requestPermissionsIfNecessary()
    │      └─ subscribeToEchoNotifications()
    │
    ├─ subscribeToEchoNotifications()
    │      └─ notificationsController.subscribeToEchoNotifications()
    │
    └─ unsubscribeFromEchoNotifications()
           └─ notificationsController.unsubscribeFromEchoNotifications()
```

### データフロー図

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

viewWillAppear()       ──────▶ updateSections()           ──────▶ tableView.reloadData()
                               │
                               └─ notificationPermissionsStatus
                               │
                               ▼
Switch ON              ──────▶ requestPushPermissions()
                               │
                               ├─ RetryBlockTask (deviceToken待ち)
                               │
                               ├─ requestPermissionsIfNecessary() → システム許可ダイアログ
                               │
                               └─ subscribeToEchoNotifications() → API呼び出し
                               │
                               ▼
Switch OFF             ──────▶ unsubscribeFromEchoNotifications() → API呼び出し
                               │
                               ▼
                               updateSections() → UI更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PushNotificationsSettingsViewController.swift | `Wikipedia/Code/PushNotificationsSettingsViewController.swift` | ソース | ViewController |
| SubSettingsViewController.swift | `Wikipedia/Code/SubSettingsViewController.swift` | ソース | 親クラス |
| WMFSettingsTableViewCell.swift | `Wikipedia/Code/WMFSettingsTableViewCell.swift` | ソース | 設定セル |
| WMFTableHeaderFooterLabelView.swift | `Wikipedia/Code/WMFTableHeaderFooterLabelView.swift` | ソース | ヘッダービュー |
| WMFNotificationsController.swift | `WMF/Sources/` | ソース | 通知コントローラ |
| RetryBlockTask.swift | `Wikipedia/Code/RetryBlockTask.swift` | ソース | リトライ処理 |
