【iOS】Swift6対応方法!Strict Concurrency Checking(厳密な並行性チェック)

この記事からわかること
- Swift6の対応方法
- Strict Concurrency Checking(厳密な並行性チェック)
- 有効にするには?
- 警告やエラーの解消方法
index
[open]
\ アプリをリリースしました /
環境
- Xcode:16.0
- iOS:18.0
- Swift:6
- macOS:Sonoma 14.6.1
公式リファレンス:Swift 6へのアプリの移行 - WWDC24 - ビデオ
Swift6からコンパイル時に検出できるように
公式リファレンス:Swift 6 アプリで厳密な並行性を採用する
Xcode16からSwift6モードのコンパイラが搭載されるようになりSwift6では厳密に並行性をチェックされるようになります。このチェックにより、「特定の再現手順の確立が容易でない場合もあるためデバッグが困難であったデータ競合の可能性」をコンパイル時に検出することが可能になりました。
Xcode16ではデフォルトではSwift6モードは有効になっていません。そのためXcodeをアップデート後に既存プロジェクトを開いても、新規プロジェクトを立ち上げても今までと変わらずSwift5モードで実行されます。
Swift6モードでのコンパイルエラーをチェックする
いきなりSwift6モードを有効にしてしまうとエラーが発生しビルドが通らなくなってしまうのでまずはコンパイルエラーになる箇所をチェックして、都度コードベースでの修正対応を行ってから有効にする流れをとります。
チェックするには「Build Settings」>「Swift Compiler - Upcoming Features」>「Strict Concurrency Checking(厳密な並行性チェック)」がMinimal
、Targeted
、Complete
の3つから選択できるようになっているのでTargeted
またはComplete
に指定します。
公式リファレンス:Setting name: SWIFT_STRICT_CONCURRENCY

Minimal:デフォルト設定。Swift Concurrencyを利用している箇所かつ明示的にSendableを定義している箇所でのみチェック
Targeted:Swift Concurrencyを利用している箇所でのみチェック
Complete:Swift Concurrencyを利用していない箇所も含めてプロジェクト全体でチェック
これでビルドを行うと例えば以下のような警告が表示されるようになります。
Stored property 'プロパティ名' of 'Sendable'-conforming class 'クラス名' has non-sendable type 'データ型'; this is an error in the Swift 6 language mode
Main actor-isolated class property 'shared' can not be referenced from a nonisolated context; this is an error in the Swift 6 language mode
Swift6モードを有効にする
実際にSwift6モードに変更するには「Build Settings」>「Swift Compiler - Language」>「Swift Language Version」をSwift6
に設定するだけです。

Swift6
に設定するとStrict Concurrency Checking
はComplete
に設定され、警告ではなくコンパイルエラーに変わりビルドができなくなります。
対応方針
Swift6にアプリを対応させていくためにはスレッドセーフな設計にしていく必要があります。スレッドセーフな設計を実装する手段はいくつかあるので要件に応じた手段で対応を追加していきます。
スレッドセーフの設計にする手段
- @MainActorを付与(メインスレッドでの操作に限定)
- Sendableに準拠(スレッドセーフ設計)
- @unchecked Sendableに準拠(DispatchQueueなどを使用しコードベースでのスレッドセーフ設計)
- actorに置き換え
パターン1:UIレイヤーの対応
例えば以下のような「ローディング機能を管理するViewModel」などUIに直接絡むクラスであれば@MainActor
を追加(メインスレッドでの操作に限定)するだけで警告が解消します。以下の場合はDispatchQueue.main.asyncAfter
の部分で参照するself
に警告が発生します。
// MARK: 順番上下ローディング
@MainActor
final class LoadingViewModel: ObservableObject {
@Published private(set) var height: Double = 0
@Published private(set) var timerPublisher: AnyCancellable?
public func onAppear() {
startLoading()
}
public func onDisappear() {
stopLoading()
}
private func startLoading() {
timerPublisher = Timer.publish(every: 0.3, on: .main, in: .common)
.autoconnect()
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
guard let self else { return }
self.height += 15
if self.height == 30 {
self.height = 0
self.timerPublisher?.cancel()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
// Task-isolated 'self' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses
guard let self else { return }
self.startLoading()
}
}
}
}
private func stopLoading() {
height = 0
timerPublisher?.cancel()
}
}
パターン2:actorでスレッドセーフ設計
スレッドセーフな設計にするためにはactor
を使用します。actor
はデータの排他制御を行うことができるスレッドセーフなオブジェクトなので置き換えるだけで対応が完了します。と言いたいところですがactor
に置き換えるとawait
を使用して呼び出す必要が出てくるので呼び出し側の改修範囲が大きくなってしまうデメリットもあります。
actor Score {
private(set) var value: Int = 0
func output(score: Int) {
print("Score:\(score)")
}
}
let score = Score()
Task {
try await Task.sleep(nanoseconds: 1 * 1_000_000_000) // 1秒停止
await score.update(score: 200)
print("非同期処理1")
}
パターン3:@unchecked Sendableでスレッドセーフをコードベースで保証
対象のクラスにSendable
をそのまま準拠できるのであればそれで良いですが準拠できない場合もあります。その際でもコードベースでスレッドセーフな設計になっている場合は@unchecked Sendable
を使用することでコンパイラに「スレッドセーフである」と認識させることで警告を抑制することも可能です。ただデータ競合が起こらないという保証はなくなるので場合によってはDispatchQueue
などを使用して正しくスレッドを管理する必要があります。
final class UserDefaultsRepository: @unchecked Sendable {
/// `UserDefaults`が`Sendable`ではないがスレッドセーフな設計
private let userDefaults: UserDefaults = UserDefaults.standard
/// Bool:保存
public func setBoolData(key: String, isOn: Bool) {
userDefaults.set(isOn, forKey: key)
}
/// Bool:取得
public func getBoolData(key: String) -> Bool {
return userDefaults.bool(forKey: key)
}
}
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。