【物語で学ぶ Swift Concurrency】数百件のエラーも怖くない！ 高校生が語る『Swift Concurrency 移行戦略』

## どこから片付ける？ 高校生の先輩後輩対話形式で解説

Swift で **Strict Concurrency Checking** を有効にしたら、コンパイルエラーが何百、あるいは何千件も出てしまった――そんな恐ろしい経験をした人、これから遭遇しそうな人へ向けて。  
ここでは高校生の「先輩」と「後輩」の対話形式で、**大量のコンパイルエラーをどこから直せばいいか** をわかりやすくお話しします。さらに、最新の Xcode ベータ版を活用する戦略も加えてご紹介！

---

## 登場人物

- **先輩（高3）**: Swift Concurrency を既に経験しており、大量エラーを乗り越えたことがある  
- **後輩（高1）**: プロジェクトを Swift Concurrency に移行しようとして、数百～数千のエラーに苦しんでいる  

---

## 場面：放課後の教室

**後輩（高1）**  
「先輩、助けてください！ Swift Concurrency の `Strict Concurrency Checking` を `Complete` にしたら、何百ものコンパイルエラーが出ちゃって、どこから直せばいいのか見当がつきません……」

**先輩（高3）**  
「あるあるだねー。僕も最初のとき、**1000件以上** のエラーが出て、頭がパンクしそうになったことあるよ。」

**後輩**  
「やっぱりそういうことあるんですね。どうやって片付けたんですか？」

**先輩**  
「僕は最初に **“大きな原因になっている箇所”** を探して、一気にエラーを減らしたんだ。いきなり全部を直そうとすると混乱するからね。」

---

## 1. まずはグローバル／静的変数を不変化しよう

**先輩**  
「一番最初に着手するといいのは、**グローバル変数や静的変数** だよ。エラー文に `global variable 'xxx' is not concurrency-safe` とか出てない？」

**後輩**  
「めっちゃ出てます！ 同じ変数があちこちで使われてて、エラーが大量に……」

**先輩**  
「そこを **`var` → `let`** に変えて不変にしたり、必要なら `@MainActor` を付けるだけで、一気にエラーが減る可能性があるよ。」

**後輩**  
「なるほど。不変化できるならそれが一番簡単ですね。」

**先輩**  
「そうそう。まずここを片付けるだけでも、**エラー数が一気に減る** と思う。最初はそこから始めよう。」

---

## 2. プロトコル／デリゲートのアイソレーション

**後輩**  
「次は何を直せばいいんでしょう？」

**先輩**  
「**プロトコルやデリゲート** が非アイソレート扱いで、実装側が `@MainActor` なせいでミスマッチが起きるエラーが多くない？」

**後輩**  
「はい。`main actor-isolated instance method cannot be used to satisfy nonisolated protocol requirement` みたいなのが大量に……」

**先輩**  
「それは **デリゲートメソッドがメインアクターで呼ばれるはずなのに、プロトコルは “どこでも呼べる” と解釈されている** から起こるんだ。  
もし UI 更新専用のデリゲートなら、**プロトコル自体を `@MainActor` にする** のが一番シンプルだよ。」

**後輩**  
「なるほど。プロトコルに `@MainActor` を付ければ、実装側や呼び出し側は `await` でメインアクターにジャンプしないと呼べなくなるわけですね。」

**先輩**  
「そうそう。もしフレームワーク側で変えられないなら、実装側で `Task { @MainActor in ... }` を使ってメインアクターへ移動する手もあるね。」

---

## 3. 同じ種類のエラーをまとめて検索して修正

**後輩**  
「エラーが多すぎるとき、同じエラーが何十件も出てたりしますよね。あれって一括で直したほうがいいんでしょうか？」

**先輩**  
「うん。**“scoreDidUpdate” が絡むエラーを全部まとめて検索して、一気に直す** とか、そんな感じでやると効率がいい。  
コンパイラの Fix-it 提案（`Fix` ボタンとか）を使うのもありだけど、まとめてやるほうが楽な場合もあるよ。」

**後輩**  
「確かに、一つずつ直すよりまとめてやったほうが早そうです。」

---

## 4. 古いライブラリとの互換性：`@preconcurrency`

**後輩**  
「うちのプロジェクト、Objective-C 由来のライブラリも使ってて、そこでもエラーがいっぱい出るんですよ……」

**先輩**  
「そういうときは、**`@preconcurrency`** を使ってみるといいかも。Swift Concurrency に対応していない古いライブラリを、いきなり厳密な並行性チェックにかけるとエラーになるからね。」

**後輩**  
「`@preconcurrency import SomeOldLib` みたいな感じですか？」

**先輩**  
「そうそう。それで一時的にエラーを抑えられる。ただ、最終的にはライブラリ側が対応してくれるのがベストだけど。」

---

## 5. 完了ハンドラから async/await への移行は最後に

**後輩**  
「じゃあ、完了ハンドラを全部 async/await に書き換えるのはいつやるんですか？」

**先輩**  
「**最後** にやるのがおすすめ。なぜなら完了ハンドラを async/await にすると、**呼び出し元も全部修正が必要** で、さらにエラーが増えるから。」

**後輩**  
「そうなんですよ。先にやろうとしたら余計にエラーが増えちゃって……」

**先輩**  
「だから、まずは**グローバル変数やプロトコルのミスマッチ**を片付けてエラーを減らしてから、**段階的に async/await** にするほうが精神的にも楽。」

---

## 6. `MainActor.assumeIsolated` は最終手段

**後輩**  
「あと `MainActor.assumeIsolated` って見かけました。あれはどう使うんですか？」

**先輩**  
「**“本当はメインアクターで呼ばれてるのに、コンパイラがわかってくれない”** ときに『ここは絶対メインアクターだ！』って強制するやつ。  
でも実際メインアクターじゃなかったらクラッシュするから、**最終手段** だね。」

**後輩**  
「なるほど……。プロトコルとか呼び出し側を直せるなら、そっちが安全なんですね。」

---

## 7. 最新の Xcode ベータ版でコンパイルする戦略

**後輩**  
「先輩、そういえば最新の Xcode ベータ版を使うといいって聞いたんですけど、本当ですか？」

**先輩**  
「うん、**最新の Xcode ベータ版** には Swift Concurrency に関する **追加の注釈** や **改善されたエラーメッセージ** が含まれている場合があるんだ。  
つまり、**コンパイラがより正確にエラーを教えてくれたり、修正候補（Fix-it）を出してくれたり** することが増える。」

**後輩**  
「へえ、それなら同じコードでもエラー数が減ったり、修正が簡単になったりするんですね？」

**先輩**  
「そうそう。だから **「とりあえず最新の安定版」じゃなくて「最新ベータ版」** を試してみるのも一つの戦略だね。もちろんベータ版だから他のバグがあるかもしれないけど、**並行処理の移行に関しては新しい注釈が役立つ** ことが多い。」

---

## 8. おすすめの戦略まとめ

1. **グローバル／静的変数を不変化（let）**  
   - ここを片付けるだけで大量のエラーが消える可能性大。  
2. **プロトコル（デリゲート）のアイソレーション**  
   - `@MainActor` をプロトコルやメソッドに付ける。  
   - できない場合は実装側で `Task { @MainActor in ... }` を使う。  
3. **似たエラーをまとめて修正**  
   - 検索して一括置換や Fix-it を活用しよう。  
4. **古いライブラリ** は `@preconcurrency`  
   - Swift Concurrency 非対応のコードを一時的にエラー回避。  
5. **完了ハンドラを async/await にするのは最後**  
   - 他のエラーが減ってから、落ち着いて書き換えよう。  
6. **どうしてもダメなら `MainActor.assumeIsolated`**  
   - コンパイラが認識できない場合の最終手段（間違うとクラッシュ）。  
7. **最新の Xcode ベータ版を試す**  
   - 新しい注釈やエラーメッセージの改善で、移行がスムーズになる可能性大。

---

## 9. エピローグ

**後輩**  
「先輩、ありがとうございます！ 言われたとおりに **グローバル変数を不変化** したり、プロトコルに `@MainActor` つけたりしてみたら、エラーが一気に減りました！ しかも最新の Xcode ベータ版を使ってみたら、Fix-it が親切になっててさらに楽に……」

**先輩**  
「よかったね。あとは落ち着いて、完了ハンドラを async/await に変えるとか、細かい箇所を直していけばいいと思う。テストを回しながらやれば、バグも減らせるし。」

**後輩**  
「わかりました。焦らず、一歩ずつやってみます！」

**先輩**  
「そうそう。**数百～数千件のエラー** も、**大きな原因を先に潰す → 似たエラーをまとめて直す → 最後に async/await** って流れでやると、意外となんとかなるよ。最新の Xcode ベータ版も積極的に使ってみるといいよ！」

---

## **まとめ**

- **エラーが何百、何千** 出ても慌てない！  
- **グローバル／静的変数** の不変化や `@MainActor` 化で、一気にエラーが減る。  
- **プロトコル（デリゲート）** のアイソレーションを見直す（`@MainActor` をつける or 実装側でメインアクターへジャンプ）。  
- **古いライブラリ** は `@preconcurrency` で一時的にエラー回避。  
- **完了ハンドラを async/await** にするのは最後にまとめてやる。  
- **最終手段** として `MainActor.assumeIsolated` を検討（実際にメインアクターじゃなければクラッシュ）。  
- **最新の Xcode ベータ版** を使うと、移行のための注釈やエラーメッセージが改善されている可能性があり、作業がスムーズになる。

この順番で進めれば、大量のコンパイルエラーも段階的に減らしていけるはず。焦らず、大きな原因から一つずつクリアしていきましょう！