Swift のアーキテクチャパターンまわりの新しい記事を眺めてると、
@Observable には、明示的に @MainActor をつけましょう。
その親 View にも @MainActor をつけるのが推奨されます。
などと書いてるのを多く見かける。
「全部 UI スレッドでは?」
などと思いながら、あれこれやってみました。
import SwiftUI
@MainActor
struct TestWebRequest: View {
@State private var responseBody = ""
//private let url = URL(string: "https://wttr.in/?format=3")!
//private var background = Background()
private var viewModel = TestViewModel()
var body: some View {
// Text("\(responseBody)")
// Text("\(background.responseBody)")
Text("\(viewModel.responseBody)")
// .onAppear { // OK
// responseBody = try! String(contentsOf: url)
// }
// .onAppear {
// // Passing argument of non-sendable type '(any URLSessionTaskDelegate)?'
// // outside of main actor-isolated context may introduce data races
// Task {
// let (data, _) = try! await URLSession.shared.data(from: url)
// responseBody = String(data: data, encoding: .utf8)!
// }
// }
// .task {
// // Passing argument of non-sendable type '(any URLSessionTaskDelegate)?'
// // outside of main actor-isolated context may introduce data races
// let (data, _) = try! await URLSession.shared.data(from: url)
// responseBody = String(data: data, encoding: .utf8)!
// }
// .onAppear {
// Task {
// responseBody = await Background.request(url: url)
// }
// }
// .task {
// responseBody = await background.request(url: url)
// }
// .task {
// responseBody = await background.request(url: url)
// }
.task {
//await background.request(url: url)
await viewModel.request()
}
}
}
@MainActor
@Observable
final class TestViewModel {
var responseBody = ""
private let background = Background()
func request() async {
responseBody = await background.request()
}
}
//class Background: @unchecked Sendable {
final class Background: Sendable {
private let url = URL(string: "https://wttr.in/?format=3")!
func request() async -> String {
let (data, _) = try! await URLSession.shared.data(from: url)
return String(data: data, encoding: .utf8)!
}
}
//actor Background {
// private let url = URL(string: "https://wttr.in/?format=3")!
//
// nonisolated func request() async -> String {
// let (data, _) = try! await URLSession.shared.data(from: url)
// return String(data: data, encoding: .utf8)!
// }
//}
#Preview {
TestWebRequest()
}
こうなりますか。
そろそろ消えてほしかった ViewModel。
いや、それとも、
おすすめ定番パターンを Apple はアナウンスしてもよくない ?
いろいろ混乱します。