AsyncStreamとは?
非同期のSequence, 要素が非同期的に流れてくる小川。 公式資料
従来はクロージャで実装していたものをSwift Concurrency
の記法、async await
で書けるように変換できるのが嬉しい。(のだと思う…)
// 以下のように定義すると
var asyncStream: AsyncStream<String> {
AsyncStream { continuation in
Task {
try? await Task.sleep(until: .now + .seconds(1))
continuation.yield("🍑") // 川に桃を流す
try? await Task.sleep(until: .now + .seconds(1))
continuation.yield("🪑") // 川に椅子を流す
try? await Task.sleep(until: .now + .seconds(1))
continuation.yield("👢") // 川に長靴を流す
}
}
}
// 以下のように使用できる。
func startStreaming() {
Task {
for await sth in asyncStream {
if sth == "🍑" {
print("桃が流れてきました。")
} else {
print("ゴミが流れてきました。")
}
}
}
}
実装例A
カメラのプレビュー用画像データを逐次流す
実装例B
タップされたらすぐに数字が増加する動機カウンターと、一カウントごとに1秒表示して数字が増加する非同期カウンターを実装する。
ContentView.swift
struct ContentView: View {
@State private var model = Model()
var body: some View {
VStack {
Text("Async: \(model.asyncCounter)")
Text("Sync: \(model.syncCounter)")
Button("Count Up") { model.countUp() }
}
.task { await model.startStreaming() }
}
}
Model.swift
@Observable
final class Model {
private(set) var syncCounter = 0
private(set) var asyncCounter = 0
private let delay: UInt64 = 1_000_000_000
func countUp() {
syncCounter += 1
throwIntoStream?(syncCounter)
}
func startStreaming() async {
for await value in counterValueStream {
try? await Task.sleep(nanoseconds: delay)
asyncCounter = value
}
}
private var throwIntoStream:((Int)->Void)?
private var counterValueStream: AsyncStream<Int> {
AsyncStream { continuation in
self.throwIntoStream = { value in
continuation.yield(value)
}
}
}
}
その他
WWDCの動画もあるらしい。(まだ見てない…。動画見るの面倒くさい…。)