【物語で学ぶ Swift Concurrency】タスクグループの使い方

## 登場人物：
- **先生**（プログラミング担当の教師）
- **生徒（ユウタ）**（高校1年生、プログラミング初心者）

---

## 📌 シーン1： タスクグループって何？

放課後、プログラミングの授業が終わった後、ユウタは先生に質問をした。

**ユウタ**「先生、Swiftで複数のAPIを同時に呼び出せるって聞いたんですけど、本当ですか？」

**先生**「本当だよ。`TaskGroup` を使うと、複数の非同期処理を**並列**で実行できるんだ。」

**ユウタ**「並列…ですか？ 普通に `await` で順番に実行するのとは違うんですか？」

**先生**「そうだね。例えば、SNSアプリで**友達のリスト**と**投稿記事のタイトル**を取得するとしよう。APIがそれぞれ分かれていて、片方が3秒、もう片方が1秒かかるとする。」

**ユウタ**「もし順番に実行したら、3秒 + 1秒で**4秒**かかりますよね？」

**先生**「その通り。でも `TaskGroup` を使えば、**同時に**実行できるから、かかる時間は**長い方の3秒だけ**で済むんだ。」

**ユウタ**「えぇ！？ そんなことができるんですか？」

**先生**「もちろんだよ。実際にコードを見てみよう。」

---

## 📌 シーン2： 実際のコードを見てみよう

先生がホワイトボードにコードを書き始める。
```swift
// TaskGroupView.swift
struct MypageInfo {
    let friends: [String]
    let airticleTitles: [String]
}
```
`struct MypageInfo { let friends: [String] let airticleTitles: [String] }`

**ユウタ**「この `MypageInfo` は何をするんですか？」

**先生**「これは、マイページの情報を**ひとつのデータ**としてまとめるための `struct` だよ。友達のリストと、投稿記事のタイトルを保持するんだ。」

**ユウタ**「なるほど。それじゃあ、実際にAPIを呼び出す部分は？」

**先生**「ここだよ。」
```swift
// 友達のリストを取得（3秒かかる）
private func fetchFriends() async -> [String] {
    await Util.wait(seconds: 3)
    return ["たかし", "ゆうた", "さくら", "けんじ"]
}

// 記事タイトルを取得（1秒かかる）
private func fetchArticles() async -> [String] {
    await Util.wait(seconds: 1)
    return ["ペットを飼い始めました", "うちの猫の名前はミケ", "ミケが勉強の邪魔をしてきます"]
}
```
**ユウタ**「え？ `await Util.wait(seconds: 3)` って何ですか？」

**先生**「これは、**ダミーの待機処理** だよ。本当なら `fetchFriends()` や `fetchArticles()` ではAPIを呼ぶんだけど、ここでは時間のかかる処理を再現するために、指定秒数待機するようにしているんだ。」

---

## 📌 シーン3： `TaskGroup` を使って並列実行

**先生**「じゃあ `TaskGroup` を使って `fetchFriends` と `fetchArticles` を**同時**に呼び出す方法を教えるね！」
```swift
// TaskGroup を使って並列に API を実行
func fetchMyPageData() async -> MypageInfo {
    var friends: [String] = []
    var articles: [String] = []

    await withTaskGroup(of: FetchType.self) { group in
        // 友達リストを取得するタスク
        group.addTask {
            let friendsList = await self.fetchFriends()
            return FetchType.friends(friendsList)
        }

        // 記事タイトルを取得するタスク
        group.addTask {
            let articlesList = await self.fetchArticles()
            return FetchType.articles(articlesList)
        }

        // タスクの結果を取得
        for await fetchResult in group {
            switch fetchResult {
            case .friends(let f):
                friends = f
            case .articles(let a):
                articles = a
            }
        }
    }

    return MypageInfo(friends: friends, airticleTitles: articles)
}
```
**ユウタ**「`withTaskGroup` って何ですか？」

**先生**「`TaskGroup` を作って、その中で `addTask` を使って**複数のタスクを並列に実行**する方法だよ。」

**ユウタ**「つまり `fetchFriends` と `fetchArticles` が同時に実行されるんですか？」

**先生**「そういうこと！このコードだと `fetchFriends` が3秒、`fetchArticles` が1秒かかるけど、`TaskGroup` を使うことで、かかる時間は**3秒だけ**で済むんだ。」

**ユウタ**「おぉー！普通に `await` で順番に実行するより、めっちゃ速いじゃないですか！」

---

## 📌 シーン4： `TaskGroup` の動作を図で理解しよう

先生がホワイトボードにタスクの流れを書いた。
```text
親タスク: fetchMyPageData()
 ├── 子タスク: fetchFriends()（3秒）
 ├── 子タスク: fetchArticles()（1秒）
```
**ユウタ**「なるほど、親タスクが2つの子タスクを**同時**に実行して、それぞれの結果を集めて `MypageInfo` を作るんですね！」

**先生**「その通り！しかも `for await` を使えば、結果を1つずつ処理できるから便利なんだよ。」

---

## 📌 シーン5： `TaskGroup` を使うメリット

**ユウタ**「先生、`TaskGroup` を使うと、どんなメリットがあるんですか？」

**先生**「主に3つあるよ！」

1. **処理時間の短縮**
   - 普通に `await` で順番に実行すると4秒かかるけど、 `TaskGroup` なら3秒で終わる！

2. **並列処理の簡単な実装**
   - `TaskGroup` を使えば、非同期処理を**簡単**に並列実行できる。

3. **エラー処理の強化**
   - もし1つのタスクがエラーになっても、他のタスクは**影響を受けずに実行を続けられる**。

---

## 🎉 まとめ

- `TaskGroup` を使うと **APIを並列実行** できる！
- `addTask` で**複数のタスクを同時に開始**できる。
- `for await` を使うと **結果を簡単に取得** できる。

**ユウタ**「なるほど！Swiftの `TaskGroup` ってすごいですね！」

**先生**「そうだね！非同期処理をスムーズに書けるようになるから、ぜひ使ってみよう！」

**ユウタ**「はい！今日の授業、めちゃくちゃ勉強になりました！」

**先生**「それはよかった！次の授業も楽しみにしていてね！」