MENU

【2025年版】Swift Concurrencyとは?初心者でもわかる非同期処理の基礎から実践まで

Swiftでアプリ開発をしていると、「ネットワーク通信中に画面が固まる」「複数の処理を同時に実行したい」といった場面に遭遇しますよね。そんなときに活躍するのがSwift Concurrencyです。

この記事では、Swift初心者の方でも理解できるように、Swift Concurrencyの基礎から実践的な使い方まで、わかりやすく解説します。

目次

Swift Concurrencyとは?

Swift Concurrencyは、Swiftプログラミング言語に組み込まれた非同期処理と並行処理を安全に扱うための機能です。Swift 5.5(2021年リリース)で導入され、iOSやmacOSアプリ開発における非同期処理の新しい標準となっています。

なぜSwift Concurrencyが必要なのか?

アプリ開発では、以下のような処理を頻繁に扱います。

  • ネットワークからのデータ取得
  • データベースへの読み書き
  • 画像の加工処理
  • ファイルの読み込み

これらの処理を同期的に実行すると、処理が完了するまでアプリが固まってしまいます。Swift Concurrencyを使えば、これらの重い処理をバックグラウンドで実行しながら、UIはスムーズに動作させることができます。

Swift Concurrencyの5つの主要機能

1. async/await – 非同期処理を読みやすく

async/awaitは、非同期処理を同期処理のように書ける構文です。

従来の書き方(コールバック)

func fetchUserData(completion: @escaping (Result<User, Error>) -> Void) {
    URLSession.shared.dataTask(with: url) { data, response, error in
        if let error = error {
            completion(.failure(error))
            return
        }
        // データ処理...
        completion(.success(user))
    }.resume()
}

// 使用時
fetchUserData { result in
    switch result {
    case .success(let user):
        print(user.name)
    case .failure(let error):
        print(error)
    }
}

Swift Concurrencyの書き方

func fetchUserData() async throws -> User {
    let (data, _) = try await URLSession.shared.data(from: url)
    let user = try JSONDecoder().decode(User.self, from: data)
    return user
}

// 使用時
Task {
    do {
        let user = try await fetchUserData()
        print(user.name)
    } catch {
        print(error)
    }
}

コードが大幅にシンプルになり、読みやすくなりました。

2. Actor – データ競合を自動的に防ぐ

Actorは、複数のスレッドから同時にアクセスされてもデータが壊れないことを保証する型です。

actor BankAccount {
    private var balance: Double = 0
    
    func deposit(_ amount: Double) {
        balance += amount
    }
    
    func withdraw(_ amount: Double) -> Bool {
        guard balance >= amount else { return false }
        balance -= amount
        return true
    }
    
    func getBalance() -> Double {
        return balance
    }
}

// 使用例
let account = BankAccount()

Task {
    await account.deposit(1000)
    let balance = await account.getBalance()
    print("残高: \(balance)円")
}

Actorを使うことで、コンパイラが自動的にデータ競合をチェックしてくれます。

3. Task – 非同期処理の実行単位

Taskは、非同期処理を実行するための基本単位です。

// 基本的なTask
Task {
    let data = try await fetchData()
    print(data)
}

// キャンセル可能なTask
let task = Task {
    for i in 1...10 {
        try Task.checkCancellation() // キャンセルチェック
        print(i)
        try await Task.sleep(nanoseconds: 1_000_000_000) // 1秒待機
    }
}

// 3秒後にキャンセル
Task {
    try await Task.sleep(nanoseconds: 3_000_000_000)
    task.cancel()
}

4. TaskGroup – 複数の処理を並行実行

TaskGroupを使うと、複数の非同期処理を並行に実行し、すべての結果を待つことができます。

func downloadImages(urls: [URL]) async throws -> [UIImage] {
    try await withThrowingTaskGroup(of: UIImage.self) { group in
        for url in urls {
            group.addTask {
                let (data, _) = try await URLSession.shared.data(from: url)
                guard let image = UIImage(data: data) else {
                    throw ImageError.invalidData
                }
                return image
            }
        }
        
        var images: [UIImage] = []
        for try await image in group {
            images.append(image)
        }
        return images
    }
}

5. MainActor – UIの更新を安全に

MainActorは、メインスレッドで実行する必要がある処理(主にUI更新)を明示的に指定します。

@MainActor
class ViewModel: ObservableObject {
    @Published var userName: String = ""
    
    func loadUser() async {
        let user = try? await fetchUserData()
        // MainActorなので、自動的にメインスレッドで実行される
        self.userName = user?.name ?? "Unknown"
    }
}

// または部分的に指定
func updateUI() async {
    let data = await fetchData() // バックグラウンドで実行
    
    await MainActor.run {
        // この中だけメインスレッドで実行
        label.text = data
    }
}

実践例:天気情報アプリ

実際のアプリ開発を想定した例を見てみましょう。

struct WeatherResponse: Codable {
    let temperature: Double
    let description: String
}

actor WeatherService {
    private let baseURL = "https://api.weather.com"
    private var cache: [String: WeatherResponse] = [:]
    
    func getWeather(for city: String) async throws -> WeatherResponse {
        // キャッシュチェック
        if let cached = cache[city] {
            return cached
        }
        
        // API呼び出し
        let url = URL(string: "\(baseURL)/weather?city=\(city)")!
        let (data, _) = try await URLSession.shared.data(from: url)
        let weather = try JSONDecoder().decode(WeatherResponse.self, from: data)
        
        // キャッシュに保存
        cache[city] = weather
        return weather
    }
}

@MainActor
class WeatherViewModel: ObservableObject {
    @Published var temperature: String = ""
    @Published var description: String = ""
    @Published var isLoading: Bool = false
    
    private let service = WeatherService()
    
    func loadWeather(for city: String) async {
        isLoading = true
        defer { isLoading = false }
        
        do {
            let weather = try await service.getWeather(for: city)
            self.temperature = "\(weather.temperature)°C"
            self.description = weather.description
        } catch {
            self.description = "読み込みエラー"
        }
    }
}

従来の方法との比較

GCD(Grand Central Dispatch)との違い

項目GCDSwift Concurrency
記述方法DispatchQueue、クロージャasync/await
可読性ネストが深くなりがち直線的で読みやすい
エラーハンドリング複雑try/catchで統一
データ競合の防止手動で管理Actorで自動保護
キャンセル手動実装が必要Task.cancel()で簡単

Combineフレームワークとの違い

Combineはリアクティブプログラミングのフレームワークで、データストリームの処理に特化しています。一方、Swift Concurrencyは一般的な非同期処理全般に使えます。

用途に応じて使い分けるのがベストですが、シンプルな非同期処理であればSwift Concurrencyの方が学習コストが低いです。

Swift Concurrency使用時の注意点

1. iOS・macOSのバージョン要件

Swift Concurrencyを使用するには以下のバージョンが必要です。

  • iOS 13.0以上
  • macOS 10.15以上
  • Swift 5.5以上

2. async関数は別のasync関数またはTask内でしか呼べない

// ❌ エラー:通常の関数からasync関数は呼べない
func normalFunction() {
    let data = await fetchData() // コンパイルエラー
}

// ✅ 正しい:Taskで囲む
func normalFunction() {
    Task {
        let data = await fetchData()
    }
}

3. Actorのプロパティへのアクセスはawaitが必要

actor Counter {
    var count = 0
}

let counter = Counter()

// ❌ エラー
print(counter.count)

// ✅ 正しい
Task {
    let value = await counter.count
    print(value)
}

まとめ

Swift Concurrencyは、非同期処理をシンプルかつ安全に書くための強力な機能です。主なポイントをおさらいしましょう。

  • async/awaitで非同期処理を同期処理のように書ける
  • Actorでデータ競合を自動的に防止できる
  • Taskで非同期処理を簡単に実行・管理できる
  • TaskGroupで複数の処理を並行実行できる
  • MainActorでUI更新を安全に行える

最初は慣れないかもしれませんが、Swift Concurrencyを使いこなせるようになると、より安全で保守性の高いコードが書けるようになります。まずは簡単なネットワーク処理から始めて、徐々に複雑な処理にも挑戦してみてください。

参考リンク

プログラミングの独学におすすめ
プログラミング言語の人気オンラインコース
独学でプログラミングを学習している方で、エラーなどが発生して効率よく勉強ができないと悩む方は多いはず。Udemyは、プロの講師が動画で実際のプログラムを動かしながら教えてくれるオンライン講座です。講座の価格は、セール期間中には専門書籍を1冊買うよりも安く済むことが多いです。新しく学びたいプログラミング言語がある方は、ぜひUdemyでオンライン講座を探してみてください。
目次