[iOS] Combine / Swift Concurrency(async/await)

非同期で呼び出す仕組み

import SwiftUI

struct ContentView: View {
    @State private var temperature: String = "__"
    @State private var windspeed: String = "__"
    @State private var showSheet = false
    
    var body: some View {
        VStack(spacing: 20) {
            Text("東京の現在天気")
                .font(.title)
            
            Text("気温: \(temperature)°C")
            Text("風速: \(windspeed)m/s")
            
            Button("天気を取得") {
                Task {
                    await fetchWeather()
                }
            }
            
            Button("詳細を表示(ボトムシート)") {
                showSheet = true
            }
        }
        .padding()
        .sheet(isPresented: $showSheet) {
            BottomSheetView(
                temperature: temperature,
                windspeed: windspeed
            )
            .presentationDetents([.fraction(0.3), .medium, .large])
            .presentationDragIndicator(.visible)
        }
    }
    
    func fetchWeather() async {
        guard let url = URL(string: "https://api.open-meteo.com/v1/forecast?latitude=35.6895&longitude=139.6917&current_weather=true") else { return }
        
        do {
            // 非同期で通信(完了を待つ)
            let (data, _) = try await URLSession.shared.data(from: url)
            let decoded = try JSONDecoder().decode(WeatherResponse.self, from: data)

            // メインスレッドでUI更新
            await MainActor.run {
                self.temperature = String(decoded.current_weather.temperature)
                self.windspeed = String(decoded.current_weather.windspeed)
            }
        } catch {
            print("エラー: \(error.localizedDescription)")
        }
    }
}


struct BottomSheetView: View {
    let temperature: String
    let windspeed: String
    
    var body: some View {
        VStack(spacing: 16) {
            Text("詳細情報")
                .font(.title3)
                .bold()
            Text("気温: \(temperature)°C")
            Text("風速: \(windspeed)m/s")
            Text("データ提供: open-meteo.com")
                .font(.footnote)
                .foregroundColor(.gray)
        }
        .padding()
    }
}

struct WeatherResponse: Codable {
    struct CurrentWeather: Codable {
        let temperature: Double
        let windspeed: Double
    }
    let current_weather: CurrentWeather
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

fetchWeather() に async を付ける 非同期処理を行う関数になる
Task { await fetchWeather() } SwiftUI のボタン内で非同期関数を呼ぶための安全な方法
try await URLSession.shared.data(from:) 通信が完了するまで待機(コールバック不要)
await MainActor.run { … } UI更新は常にメインスレッドで行う

うん、asyncは他の言語でもよく出てくるのでわかりやすい。