🟣

【Xcode,Swift】スレッドに関する紫色の警告について

2024/12/01に公開

以下の警告について解説します。
※今回の記事では、「async/await」のみに対応しています。

「Publishing changes from background threads is not allowed;」

この警告は、バックグラウンドスレッドで行われた処理がUI更新を伴う場合に発生します。Swiftでは、スレッドごとに役割が分担されており、特にUI関連の処理はメインスレッドで実行する必要があります。

スレッドの役割

メインスレッド

メインスレッドは、アプリケーションのUI表示ユーザー操作の応答を処理するスレッドです。UI関連の処理は必ずこのスレッドで実行される必要があります。

バックグラウンドスレッド

バックグラウンドスレッドは、時間がかかる処理を実行するためのスレッドです。これにより、メインスレッドの負担が軽減され、アプリのUIがスムーズに動作します。

サーバーと通信する処理など時間がかかるタスクの場合は、Swiftが自動的にバックグラウンドスレッドに回すことで、UIの表示を遅延することなく、アプリ全体で効率的なタスク処理を実現しています。

警告の原因

この警告が表示されるのは、バックグラウンドスレッドで処理されたタスクが、メインスレッドで行うべきUI関連の処理を実行しようとした場合です。

Swiftでは、バックグラウンドスレッドでUI更新を試みるとこのような問題が発生します。例えば、以下のようなコードは警告の原因になります:

func fetchData() async throws -> String {
    // 時間がかかる処理(例: サーバー通信)
    try await Task.sleep(nanoseconds: 1_000_000_000) // 1秒待機
    return "サーバーから取得したデータ"
}

func performTask() {
    Task {
        do {
            // バックグラウンドでデータ取得
            let result = try await fetchData()
            
            // バックグラウンドスレッドでUI更新
            label.text = result  // 🟣メインスレッド以外でUI操作 → 警告発生
        } catch {
            print("データ取得エラー: \(error)")
        }
    }
}

解決方法

UI更新など、メインスレッドで処理する必要があるコードに対して、「@MainActor」を用いることで問題を解決できます。

@MainActor

@MainActorを使用すると、そのメソッドがメインスレッドで実行されることを保証できます。

// @MainActorのメソッド内でUI関連の処理を行うようにする
@MainActor
func updateLabel(with text: String) {
    label.text = text
}

func fetchData() async throws -> String {
    // 時間がかかる処理(例: サーバー通信)
    try await Task.sleep(nanoseconds: 1_000_000_000) // 1秒待機
    return "サーバーから取得したデータ"
}

func performTask() {
    Task {
        do {
            // バックグラウンドでデータ取得
            let result = try await fetchData()
            
            // メインスレッドでUI更新
            await updateLabel(with: result)
        } catch {
            print("データ取得エラー: \(error)")
        }
    }
}

@MainActorを使うことで、明示的に「メインスレッドで実行する必要がある」ことを示せます。

Discussion