どこから片付ける? 高校生の先輩後輩対話形式で解説
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. おすすめの戦略まとめ
-
グローバル/静的変数を不変化(let)
- ここを片付けるだけで大量のエラーが消える可能性大。
-
プロトコル(デリゲート)のアイソレーション
-
@MainActor
をプロトコルやメソッドに付ける。 - できない場合は実装側で
Task { @MainActor in ... }
を使う。
-
-
似たエラーをまとめて修正
- 検索して一括置換や Fix-it を活用しよう。
-
古いライブラリ は
@preconcurrency
- Swift Concurrency 非対応のコードを一時的にエラー回避。
-
完了ハンドラを async/await にするのは最後
- 他のエラーが減ってから、落ち着いて書き換えよう。
-
どうしてもダメなら
MainActor.assumeIsolated
- コンパイラが認識できない場合の最終手段(間違うとクラッシュ)。
-
最新の Xcode ベータ版を試す
- 新しい注釈やエラーメッセージの改善で、移行がスムーズになる可能性大。
9. エピローグ
後輩
「先輩、ありがとうございます! 言われたとおりに グローバル変数を不変化 したり、プロトコルに @MainActor
つけたりしてみたら、エラーが一気に減りました! しかも最新の Xcode ベータ版を使ってみたら、Fix-it が親切になっててさらに楽に……」
先輩
「よかったね。あとは落ち着いて、完了ハンドラを async/await に変えるとか、細かい箇所を直していけばいいと思う。テストを回しながらやれば、バグも減らせるし。」
後輩
「わかりました。焦らず、一歩ずつやってみます!」
先輩
「そうそう。数百~数千件のエラー も、大きな原因を先に潰す → 似たエラーをまとめて直す → 最後に async/await って流れでやると、意外となんとかなるよ。最新の Xcode ベータ版も積極的に使ってみるといいよ!」
まとめ
- エラーが何百、何千 出ても慌てない!
-
グローバル/静的変数 の不変化や
@MainActor
化で、一気にエラーが減る。 -
プロトコル(デリゲート) のアイソレーションを見直す(
@MainActor
をつける or 実装側でメインアクターへジャンプ)。 -
古いライブラリ は
@preconcurrency
で一時的にエラー回避。 - 完了ハンドラを async/await にするのは最後にまとめてやる。
-
最終手段 として
MainActor.assumeIsolated
を検討(実際にメインアクターじゃなければクラッシュ)。 - 最新の Xcode ベータ版 を使うと、移行のための注釈やエラーメッセージが改善されている可能性があり、作業がスムーズになる。
この順番で進めれば、大量のコンパイルエラーも段階的に減らしていけるはず。焦らず、大きな原因から一つずつクリアしていきましょう!