今日現在、以下の設定でやっています。
🧑🏻💻 お天気情報を取得する
無料で公開されている WEB API を使います。
シンプルに取得する。
import SwiftUI
struct TestWeatherView: View {
private let url = URL(string: "https://wttr.in/?format=3")!
var body: some View {
Text(try! String(contentsOf: url))
}
}
#Preview {
TestWeatherView()
}
ちょっと窮屈なので砕いていきます。
struct TestWeatherView: View {
@State private var text = ""
private let url = URL(string: "https://wttr.in/?format=3")!
var body: some View {
Text(text)
.onAppear {
text = try! String(contentsOf: url)
}
}
}
OK です。
次は、Web クライアントを汎用性のある URLSession
に変えます。
struct TestWeatherView: View {
@State private var text = ""
private let url = URL(string: "https://wttr.in/?format=3")!
var body: some View {
Text(text)
.task {
let (data, _) = try! await URLSession.shared.data(from: url)
text = String(data: data, encoding: .utf8)!
}
}
}
ここで、警告がでます。
Passing argument of non-sendable type '(any URLSessionTaskDelegate)?' outside of main actor-isolated context may introduce data races
これは、なんですか。
🤔 Passing argument of non-sendable type '(any URLSessionTaskDelegate)?' outside of main actor-isolated context may introduce data races
URLSession
のドキュメントを見ておきます。
func data(from url: URL) async throws -> (Data, URLResponse)
👉 data(from:) | Apple Developer Documentation
Task
内の以下の部分
let (data, _) = try! await URLSession.shared.data(from: url)
左辺はメインスレッド、
--- 境界 ---
右辺はバックグラウンドスレッド
👉 【Swift】concurrency をマスターするための一つのきっかけ
ということで、
いわゆる「アクター境界」を越えているので、
data()
の返り値は Sendable
でなければなりません。
リファレンスやコードを追いかけてみると、
Data
, URLResponse
共に Sendable
に準拠しています。
👉 Data | Apple Developer Documentation
👉 URLResponse | Apple Developer Documentation
あ、Tuple
かな ?
などと思いましたがなんか違う。
👉 Pitch: User-defined tuple conformances - Evolution / Pitches - Swift Forums
🧑🏻💻 Actor を使う
Actor
は Sendable
に準拠しています。
なので、これでバックグラウンドの通信部分をラップします。
actor WeatherA {
// OK
static func getData(url: URL) async -> (Data, URLResponse) {
try! await URLSession.shared.data(from: url)
}
// OK
nonisolated func getData(url: URL) async -> (Data, URLResponse) {
try! await URLSession.shared.data(from: url)
}
}
これで、無警告でOKとなりました。
🧑🏻💻 extension 化してメインスレッドを避ける
こんな方法でもいけます。
extension URLSession {
func dataEx(url: URL) async -> (Data, URLResponse) {
try! await data(from: url)
}
}
🧑🏻💻 まとめ
分かれば、なるほど感あるけど、分からなければ全く謎で時間だけ食うので、
そんな誰かと自分用のメモとして。
class でも警告なしでいけるようです。
「Actor
はデータの非同期操作のために作られてる」感じがする。
外野から使ってみた雰囲気だけだけれども。
🧑🏻💻 参考