AsyncTimeSequences
This is a convenient package to add missing time async sequences such as debounce, throttle, delay, timeout and measure interval.
These sequences work with any AsyncSequence (such as AsyncStream), and as they conform to the AsyncSequence Protocol the possibilities are endless.
This library relies on an AsynScheduler-conforming class that guarantees async execution order. Due to the nature of the Swift Async architecture, the execution of Tasks is not deterministic and to guarantee the order of time operations it becomes necessary to add further handling. This is critical for many time operators such as Debounce and Delay. The AsyncScheduler class provided in this library promises async task execution following FIFO and cancellation.
Compatibility
This package is supported on Xcode 13.2+ targeting iOS 13+, MacOS 10.15+, WatchOS 6+ and TvOS 13+.
How to use
For all examples, please first consider this sample sequence (remember that you can use any AsyncSequence):
let asyncSequence = AsyncStream { (continuation:AsyncStream<Int>.Continuation) in
Task {
let items = [1,2,3,4,5,6]
for item in items {
continuation.yield(item)
try? await Task.sleep(nanoseconds: 1000000)
}
continuation.finish()
}
}
All the AsyncTimeSequences will need an AsynScheduler object. For convenience, there is one already bundled with this package. You should be good with the main one provided. But you are free to add your custom one if required (by conforming to the AsyncScheduler protocol).
Timeout
asyncSequence.timeout(for: 2, scheduler: MainAsyncScheduler.default)
Delay
asyncSequence.delay(for: 3, scheduler: MainAsyncScheduler.default)
Debounce
asyncSequence.debounce(for: 3, scheduler: MainAsyncScheduler.default)
Throttle
asyncSequence.throttle(for: 3, scheduler: MainAsyncScheduler.default, latest: true)
MeasureInterval
asyncSequence.measureInterval(using: MainAsyncScheduler.default)
How to test
Properly testing these time sequences requires some setup. Ideally, it is recommended to inject the scheduler, that will execute the time handling of your sequences, into your logic object.
By injecting the scheduler, you can for example inject a test scheduler to manipulate the time operators.
It is recommended to use the TestAsyncScheduler included in the AsyncTimeSequencesSupport sub-package. It has some really convenient functions to manipulate time:
let scheduler = AsyncTestScheduler()
scheduler.advance(by: 3.0) // Advances the time virtually and executes scheduled jobs immediately without actually waiting the time interval specified
An example on how to inject the schduler if you have a view model:
final class MyViewModel {
private let scheduler: AsyncScheduler
init(
scheduler: AsyncScheduler = MainAsyncScheduler.default // Allow injection while providing a default scheduler
) {
self.scheduler = scheduler
}
func debounceSequence<T: AsyncSequence>(_ sequence: T) {
let debounceSequence = sequence.debounce(for: 3.0, scheduler: scheduler)
Task {
for await value in debounceSequence {
// do something that produces an output which can be evaluated and asserted during testing...
}
}
}
}
import AsyncTimeSequences
import AsyncTimeSequencesSupport
...
func testAsyncDebounceSequence() async {
// Given
let scheduler = TestAsyncScheduler()
let viewModel = MyViewModel(scheduler: scheduler)
let items = [1,5,10,15,20]
let expectedItems = [20]
let baseDelay = 3.0
var receivedItems = [Int]()
// When
let sequence = ControlledDataSequence(items: items)
viewModel.debounceSequence()
// If we don't wait for jobs to get scheduled, advancing the scheduler does virtually nothing...
await sequence.iterator.waitForItemsToBeSent(items.count)
await scheduler.advance(by: baseDelay)
// your code to process the view model output...
}
If you need further code examples, you can take a look at the tests for this package library. They rely heavily on the AsyncTestScheduler and the ControlledDataSequence classes, which are included in the AsyncTimeSequencesSupport sub-package.
Installation
Swift Package Manager
In Xcode, select File --> Swift Packages --> Add Package Dependency and then add the following url:
https://github.com/Henryforce/AsyncTimeSequences
There are two package included:
- AsyncTimeSequences - async time sequences extensions
- AsyncTimeSequencesSupport - async-time-sequences support classes for testing. (Recommended to include only in your test targets)