Created
December 1, 2022 00:16
-
-
Save YusukeHosonuma/d8381a8d5fa2e6be31c47c3c9afd958d to your computer and use it in GitHub Desktop.
Actor / AsyncStream を使ったカウントダウン的な(とくに意味はない)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import Combine | |
struct ContentView: View { | |
@State var id: Int = 0 | |
var body: some View { | |
VStack { | |
CounterView(from: 10).id(id) | |
Button("RESET") { id += 1 } | |
} | |
} | |
} | |
struct CounterView: View { | |
@StateObject private var viewModel: CounterViewModel | |
init(from: Int) { | |
_viewModel = .init(wrappedValue: .init(from: from)) | |
} | |
var body: some View { | |
VStack { | |
Text("\(viewModel.count)") | |
.font(.largeTitle) | |
} | |
} | |
} | |
@MainActor | |
final class CounterViewModel: ObservableObject { | |
private let countdownTimer: CountdownModel | |
private var cancellables = Set<AnyCancellable>() | |
@Published var count: Int | |
init(from: Int) { | |
_count = .init(initialValue: from) | |
countdownTimer = .init(from: from) | |
Task { [weak self] in | |
guard let countSequence = await self?.countdownTimer.count else { return } | |
for await count in countSequence { | |
self?.count = count | |
} | |
} | |
.store(in: &cancellables) | |
} | |
deinit { print("🧹 CounterViewModel") } | |
} | |
actor CountdownModel { | |
private let countSubject = PassthroughSubject<Int, Never>() | |
private let from: Int | |
private var countTask: Task<Void, Never>? | |
private var cancellables: Set<AnyCancellable> = [] | |
lazy var count = AsyncStream<Int> { continuation in | |
countSubject | |
.sink { count in | |
continuation.yield(count) | |
} | |
.store(in: &cancellables) | |
continuation.onTermination = { _ in | |
Task { await self.onTermination() } | |
} | |
} | |
init(from: Int) { | |
self.from = from | |
Task { await start() } | |
} | |
func start() { | |
countTask = Task { | |
for n in (0...from).reversed() { | |
countSubject.send(n) | |
try? await Task.sleep(nanoseconds: 1_000_000_000) | |
} | |
} | |
} | |
func onTermination() { | |
countTask?.cancel() | |
} | |
deinit { print("🧹 CountdownModel") } | |
} | |
extension Task { | |
func store(in cancellables: inout Set<AnyCancellable>) { | |
AnyCancellable { self.cancel() }.store(in: &cancellables) | |
} | |
private func asCancellable() -> AnyCancellable { | |
.init { self.cancel() } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
メモリ回収の流れ(想定):