Continuous clock...or not continuous clock? That is the question. - Hamlet #108
-
DescriptionI have an issue with continuous clock in the following small snippet of code. public final class Arbitrator<Value: Equatable> {
@Dependency(\.continuousClock) var continuousClock
private var currentTask: Task<Void, Never>?
private var previousValue: Value?
private let debounceInterval: TimeInterval
public var ruling: @MainActor (Value) -> Void = { _ in }
public init(debounceInterval: TimeInterval) {
self.debounceInterval = debounceInterval
}
public func disclose(_ newValue: Value) {
currentTask?.cancel()
currentTask = Task { [weak self] in
guard let self else { return }
try? await continuousClock.sleep(for: .seconds(debounceInterval))
if newValue != self.previousValue {
await self.ruling(newValue)
self.previousValue = newValue
}
}
}
} When running the following test func testDebouncing() throws {
// Given - an arbitrator
let arbitrator = Arbitrator<String>(debounceInterval: 1)
var outcome: String?
var count: Int = 0
arbitrator.ruling = { coalesced in
count += 1
outcome = coalesced
}
// When - we discolse several input values
// in quick succession
withDependencies {
$0.continuousClock = .immediate
} operation: {
arbitrator.disclose("H")
arbitrator.disclose("He")
arbitrator.disclose("Hel")
arbitrator.disclose("Hell")
arbitrator.disclose("Hello")
}
// Then - only one ruling is provided
XCTAssertTrue(count == 1)
// And - the ruling is the last value disclosed
XCTAssertEqual(outcome, "Hello")
} If I remove main actor, and restart Xcode, I sometimes see the following.
👆 Massive dump parsed via ChatGPT ChatGPT 🤖 The provided log details a crash in the Swift compiler while trying to build the "swift-toolbox-pro" project. Here are the highlights that you should pay particular attention to: Look into the code around lines 50 to 52 in "ArbitratorTests.swift". The issue might be related to the specific code block indicated in the log, specifically the part involving the continuousClock property._ Checklist
Expected behaviorNo error. Test passes. Actual behaviorError. Test fails. Steps to reproduce
Dependencies version information0.6.0 Destination operating systemiOS 17 Xcode version informationVersion 15.0 beta 5 (15A5209g) Swift Compiler version informationswift-driver version: 1.87 Apple Swift version 5.9 (swiftlang-5.9.0.124.4 clang-1500.0.38.1) |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 7 replies
-
Hi @nashysolutions, when I run the code you provided I get a different set of errors:
Can you provide a project that demonstrates the behavior you are seeing? Also I don't think this is an issue with the library so I am going to convert to a discussion. |
Beta Was this translation helpful? Give feedback.
-
Avoiding the clock for now. The following works well. import Foundation
import Combine
import Dependencies
public final class Arbitrator<Value: Equatable> {
private var subject: PassthroughSubject<Value, Never>
private var subscription: AnyCancellable?
private var previousValue: Value?
public var ruling: (Value) -> Void = { _ in }
public init(debounceInterval: TimeInterval) {
subject = PassthroughSubject()
@Dependency(\.mainRunLoop) var mainRunLoop
subscription = subject
.debounce(for: .seconds(debounceInterval), scheduler: mainRunLoop)
.sink { [weak self] newValue in
guard let self else {
return
}
if newValue != previousValue {
ruling(newValue)
previousValue = newValue
}
}
}
public func disclose(_ newValue: Value) {
subject.send(newValue)
}
} I think the above approach using Task was flawed anyway. |
Beta Was this translation helpful? Give feedback.
For reference, here's the issue: swiftlang/swift#60552