diff --git a/.gitignore b/.gitignore
index 330d167..08003cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,7 +44,7 @@ playground.xcworkspace
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
-# .swiftpm
+.swiftpm/
.build/
@@ -88,3 +88,6 @@ fastlane/test_output
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/
+
+# Ignore DS Store from Mac
+.DS_Store
diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index 919434a..0000000
--- a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/AsyncTimeSequences.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/AsyncTimeSequences.xcscheme
deleted file mode 100644
index bde1480..0000000
--- a/.swiftpm/xcode/xcshareddata/xcschemes/AsyncTimeSequences.xcscheme
+++ /dev/null
@@ -1,91 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/FormatScript.sh b/FormatScript.sh
deleted file mode 100755
index 32104c0..0000000
--- a/FormatScript.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-
-swift-format format --in-place --recursive Sources --configuration SwiftFormatConfiguration.json
-
-swift-format format --in-place --recursive Tests --configuration SwiftFormatConfiguration.json
-
diff --git a/Package.swift b/Package.swift
index aa5a408..3af66f8 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,47 +1,47 @@
-// swift-tools-version:5.5
+// swift-tools-version:6.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
- name: "AsyncTimeSequences",
- platforms: [.iOS(.v13), .macOS(.v10_15), .watchOS(.v6), .tvOS(.v13)],
- products: [
- // Products define the executables and libraries a package produces, and make them visible to other packages.
- .library(
- name: "AsyncTimeSequences",
- targets: ["AsyncTimeSequences"]
- ),
- .library(
- name: "AsyncTimeSequencesSupport",
- targets: ["AsyncTimeSequencesSupport"]
- ),
- ],
- dependencies: [
- // Dependencies declare other packages that this package depends on.
- ],
- targets: [
- // Targets are the basic building blocks of a package. A target can define a module or a test suite.
- // Targets can depend on other targets in this package, and on products in packages this package depends on.
- .target(
- name: "AsyncTimeSequences",
- dependencies: [],
- path: "Sources/AsyncTimeSequences"
- ),
- .target(
- name: "AsyncTimeSequencesSupport",
- dependencies: [
- "AsyncTimeSequences",
- ],
- path: "Sources/AsyncTimeSequencesSupport"
- ),
- .testTarget(
- name: "AsyncTimeSequencesTests",
- dependencies: [
- "AsyncTimeSequences",
- "AsyncTimeSequencesSupport"
- ],
- path: "Tests"
- ),
- ]
+ name: "AsyncTimeSequences",
+ platforms: [.iOS(.v13), .macOS(.v13), .watchOS(.v6), .tvOS(.v13)],
+ products: [
+ // Products define the executables and libraries a package produces, and make them visible to other packages.
+ .library(
+ name: "AsyncTimeSequences",
+ targets: ["AsyncTimeSequences"]
+ ),
+ .library(
+ name: "AsyncTimeSequencesSupport",
+ targets: ["AsyncTimeSequencesSupport"]
+ ),
+ ],
+ dependencies: [
+ // Dependencies declare other packages that this package depends on.
+ ],
+ targets: [
+ // Targets are the basic building blocks of a package. A target can define a module or a test suite.
+ // Targets can depend on other targets in this package, and on products in packages this package depends on.
+ .target(
+ name: "AsyncTimeSequences",
+ dependencies: [],
+ path: "Sources/AsyncTimeSequences"
+ ),
+ .target(
+ name: "AsyncTimeSequencesSupport",
+ dependencies: [
+ "AsyncTimeSequences"
+ ],
+ path: "Sources/AsyncTimeSequencesSupport"
+ ),
+ .testTarget(
+ name: "AsyncTimeSequencesTests",
+ dependencies: [
+ "AsyncTimeSequences",
+ "AsyncTimeSequencesSupport",
+ ],
+ path: "Tests"
+ ),
+ ]
)
diff --git a/Sources/AsyncTimeSequences/AsyncScheduler/AsyncScheduler.swift b/Sources/AsyncTimeSequences/AsyncScheduler/AsyncScheduler.swift
index d5ebe1b..1fbaf8f 100644
--- a/Sources/AsyncTimeSequences/AsyncScheduler/AsyncScheduler.swift
+++ b/Sources/AsyncTimeSequences/AsyncScheduler/AsyncScheduler.swift
@@ -7,7 +7,7 @@
import Foundation
-public typealias AsyncSchedulerHandler = () async -> Void
+public typealias AsyncSchedulerHandler = @Sendable () async -> Void
public protocol AsyncScheduler: Actor {
var now: TimeInterval { get }
@@ -23,6 +23,7 @@ public actor MainAsyncScheduler: AsyncScheduler {
lazy var idCounter: UInt = 0
lazy var completedElementIds = Set()
lazy var cancelledElementIds = Set()
+ private var isCompletingElement = false
public var now: TimeInterval {
Date().timeIntervalSince1970
@@ -34,8 +35,6 @@ public actor MainAsyncScheduler: AsyncScheduler {
/// - parameter handler: async closure to be executed when 'after' time elapses
///
/// - Returns: reference to a Task which supports cancellation
- ///
- /// - Complexity: O(log n) where n is the number of elements currently scheduled
@discardableResult
public func schedule(
after: TimeInterval,
@@ -52,19 +51,52 @@ public actor MainAsyncScheduler: AsyncScheduler {
increaseCounterId()
- return Task {
- try? await Task.sleep(nanoseconds: UInt64(after * 1_000_000_000))
- await complete(currentId: currentId, cancelled: Task.isCancelled)
- }
+ return createScheduledExecutionTask(currentId: currentId, after: after)
}
/// Based on the timeIntervalSince1970 from Date, the smallest intervals will need
/// to complete before other elements' handlers can be executed. Due to the nature
/// of Tasks, there could be some situations where some tasks scheduled to finish
/// before others finish first. This could potentially have unwanted behaviors on
- /// objects scheduling events. To address this matter, a minimum priority queue
- /// is critical to always keep the first element that should be completed in the
- /// top of the queue. Once its task completes, a Set will keep track of all
+ /// objects scheduling events.
+ ///
+ /// - parameter currentId: integer variable denoting handler/task id
+ ///
+ /// - Returns: reference to a Task which supports cancellation
+ private func createScheduledExecutionTask(
+ currentId: UInt,
+ after: TimeInterval
+ ) -> Task {
+ return Task {
+ try? await Task.sleep(for: .seconds(after))
+
+ completedElementIds.insert(currentId)
+ if Task.isCancelled {
+ cancelledElementIds.insert(currentId)
+ }
+
+ // Make sure that only one complete method is running at all times.
+ // The reason why this is important is because there is an inner await in a while loop which
+ // releases the execution of this actor and it cause race conditions if another scheduled
+ // task completes within the time this method is executing causing a weird state where two
+ // while loops might have erroneous values and destroy the serial execution intended from
+ // this method.
+ guard !isCompletingElement else { return }
+
+ // Block any other Tasks from calling complete from this point.
+ isCompletingElement = true
+
+ await complete(currentId: currentId)
+
+ // Allow any future callers of this method to call complete.
+ isCompletingElement = false
+ }
+ }
+
+ /// This method runs the completion handler for a given scheduled item matching the `currentId`.
+ ///
+ /// A minimum priority queue is critical to always keep the first element that should be
+ /// completed in the top of the queue. Once its task completes, a Set will keep track of all
/// completed ID tasks that are yet to be executed. If the current top element of
/// the queue has already completed, its closure will execute. This will repeat
/// until all completed top elements of the queue are executed.
@@ -72,16 +104,16 @@ public actor MainAsyncScheduler: AsyncScheduler {
/// introduced to some scheduled async-closures. Ideally, this would be in the
/// order of micro/nanoseconds depending of the system load.
///
- /// - parameter currentId: integer variable denoting handler/task id
- /// - parameter cancelled: boolean flag required to determine whether or not to execute the handler
+ /// This method will execute an inner loop resolving all available completed elements.
+ ///
+ /// Note that his actor switches execution and during this `paused` time another scheduled task
+ /// can complete and call `complete`. It is important to have only one `complete` running at
+ /// all times (specifically, due the inner while loop).
+ ///
+ /// This method should only be called from within `createScheduledExecutionTask`.
///
/// - Complexity: O(log n) where n is the number of elements currently scheduled
- private func complete(currentId: UInt, cancelled: Bool) async {
- completedElementIds.insert(currentId)
- if cancelled {
- cancelledElementIds.insert(currentId)
- }
-
+ private func complete(currentId: UInt) async {
while let minElement = queue.peek, completedElementIds.contains(minElement.id) {
queue.removeFirst()
completedElementIds.remove(minElement.id)
diff --git a/Sources/AsyncTimeSequences/AsyncScheduler/AsyncSchedulerHandlerElement.swift b/Sources/AsyncTimeSequences/AsyncScheduler/AsyncSchedulerHandlerElement.swift
index 0bdf1b7..68b3b86 100644
--- a/Sources/AsyncTimeSequences/AsyncScheduler/AsyncSchedulerHandlerElement.swift
+++ b/Sources/AsyncTimeSequences/AsyncScheduler/AsyncSchedulerHandlerElement.swift
@@ -16,7 +16,7 @@ struct AsyncSchedulerHandlerElement {
extension AsyncSchedulerHandlerElement: Comparable {
static func < (lhs: AsyncSchedulerHandlerElement, rhs: AsyncSchedulerHandlerElement) -> Bool {
if lhs.time == rhs.time {
- return lhs.id <= rhs.id
+ return lhs.id < rhs.id
}
return lhs.time < rhs.time
}
diff --git a/Sources/AsyncTimeSequences/Debounce/AsyncDebounceSequence.swift b/Sources/AsyncTimeSequences/Debounce/AsyncDebounceSequence.swift
index 08d629e..0a6f1b7 100644
--- a/Sources/AsyncTimeSequences/Debounce/AsyncDebounceSequence.swift
+++ b/Sources/AsyncTimeSequences/Debounce/AsyncDebounceSequence.swift
@@ -8,7 +8,7 @@
import Combine
import Foundation
-public struct AsyncDebounceSequence {
+public struct AsyncDebounceSequence where Base.Element: Sendable {
@usableFromInline
let base: Base
@@ -26,7 +26,7 @@ public struct AsyncDebounceSequence {
}
}
-extension AsyncSequence {
+extension AsyncSequence where Element: Sendable {
@inlinable
public __consuming func debounce(
for interval: TimeInterval,
@@ -36,7 +36,7 @@ extension AsyncSequence {
}
}
-extension AsyncDebounceSequence: AsyncSequence {
+extension AsyncDebounceSequence: AsyncSequence where Base.Element: Sendable {
public typealias Element = Base.Element
/// The type of iterator that produces elements of the sequence.
@@ -107,7 +107,7 @@ extension AsyncDebounceSequence: AsyncSequence {
}
@usableFromInline
- struct Debounce {
+ struct Debounce: @unchecked Sendable {
private var baseIterator: Base.AsyncIterator
private let actor: DebounceActor
@@ -138,13 +138,13 @@ extension AsyncDebounceSequence: AsyncSequence {
@inlinable
public __consuming func makeAsyncIterator() -> AsyncStream.Iterator {
return AsyncStream { (continuation: AsyncStream.Continuation) in
+ var debounce = Debounce(
+ baseIterator: base.makeAsyncIterator(),
+ continuation: continuation,
+ interval: interval,
+ scheduler: scheduler
+ )
Task {
- var debounce = Debounce(
- baseIterator: base.makeAsyncIterator(),
- continuation: continuation,
- interval: interval,
- scheduler: scheduler
- )
await debounce.start()
}
}.makeAsyncIterator()
diff --git a/Sources/AsyncTimeSequences/Delay/AsyncDelaySequence.swift b/Sources/AsyncTimeSequences/Delay/AsyncDelaySequence.swift
index f3d162f..032c4fb 100644
--- a/Sources/AsyncTimeSequences/Delay/AsyncDelaySequence.swift
+++ b/Sources/AsyncTimeSequences/Delay/AsyncDelaySequence.swift
@@ -8,7 +8,7 @@
import Combine
import Foundation
-public struct AsyncDelaySequence {
+public struct AsyncDelaySequence where Base.Element: Sendable {
@usableFromInline
let base: Base
@@ -26,7 +26,7 @@ public struct AsyncDelaySequence {
}
}
-extension AsyncSequence {
+extension AsyncSequence where Element: Sendable {
@inlinable
public __consuming func delay(
for interval: TimeInterval,
@@ -36,7 +36,7 @@ extension AsyncSequence {
}
}
-extension AsyncDelaySequence: AsyncSequence {
+extension AsyncDelaySequence: AsyncSequence where Base.Element: Sendable {
public typealias Element = Base.Element
/// The type of iterator that produces elements of the sequence.
@@ -97,7 +97,7 @@ extension AsyncDelaySequence: AsyncSequence {
}
@usableFromInline
- struct Delay {
+ struct Delay: @unchecked Sendable {
private var baseIterator: Base.AsyncIterator
private let actor: DelayActor
@@ -128,13 +128,13 @@ extension AsyncDelaySequence: AsyncSequence {
@inlinable
public __consuming func makeAsyncIterator() -> AsyncStream.Iterator {
return AsyncStream { (continuation: AsyncStream.Continuation) in
+ var delay = Delay(
+ baseIterator: base.makeAsyncIterator(),
+ continuation: continuation,
+ interval: interval,
+ scheduler: scheduler
+ )
Task {
- var delay = Delay(
- baseIterator: base.makeAsyncIterator(),
- continuation: continuation,
- interval: interval,
- scheduler: scheduler
- )
await delay.start()
}
}.makeAsyncIterator()
diff --git a/Sources/AsyncTimeSequences/MeasureInterval/AsyncMeasureIntervalSequence.swift b/Sources/AsyncTimeSequences/MeasureInterval/AsyncMeasureIntervalSequence.swift
index f334f4b..8579531 100644
--- a/Sources/AsyncTimeSequences/MeasureInterval/AsyncMeasureIntervalSequence.swift
+++ b/Sources/AsyncTimeSequences/MeasureInterval/AsyncMeasureIntervalSequence.swift
@@ -8,7 +8,7 @@
import Combine
import Foundation
-public struct AsyncMeasureIntervalSequence {
+public struct AsyncMeasureIntervalSequence where Base.Element: Sendable {
@usableFromInline
let base: Base
@@ -22,7 +22,7 @@ public struct AsyncMeasureIntervalSequence {
}
}
-extension AsyncSequence {
+extension AsyncSequence where Element: Sendable {
@inlinable
public __consuming func measureInterval(
using scheduler: AsyncScheduler
@@ -31,7 +31,7 @@ extension AsyncSequence {
}
}
-extension AsyncMeasureIntervalSequence: AsyncSequence {
+extension AsyncMeasureIntervalSequence: AsyncSequence where Base.Element: Sendable {
public typealias Element = TimeInterval
/// The type of iterator that produces elements of the sequence.
@@ -72,7 +72,7 @@ extension AsyncMeasureIntervalSequence: AsyncSequence {
}
@usableFromInline
- struct MeasureInterval {
+ struct MeasureInterval: @unchecked Sendable {
private var baseIterator: Base.AsyncIterator
private let actor: MeasureIntervalActor
@@ -101,12 +101,12 @@ extension AsyncMeasureIntervalSequence: AsyncSequence {
@inlinable
public __consuming func makeAsyncIterator() -> AsyncStream.Iterator {
return AsyncStream { (continuation: AsyncStream.Continuation) in
+ var measureInterval = MeasureInterval(
+ baseIterator: base.makeAsyncIterator(),
+ continuation: continuation,
+ scheduler: scheduler
+ )
Task {
- var measureInterval = MeasureInterval(
- baseIterator: base.makeAsyncIterator(),
- continuation: continuation,
- scheduler: scheduler
- )
await measureInterval.start()
}
}.makeAsyncIterator()
diff --git a/Sources/AsyncTimeSequences/Throttle/AsyncThrottleSequence.swift b/Sources/AsyncTimeSequences/Throttle/AsyncThrottleSequence.swift
index ca0b120..a4cd036 100644
--- a/Sources/AsyncTimeSequences/Throttle/AsyncThrottleSequence.swift
+++ b/Sources/AsyncTimeSequences/Throttle/AsyncThrottleSequence.swift
@@ -8,7 +8,7 @@
import Combine
import Foundation
-extension AsyncSequence {
+extension AsyncSequence where Element: Sendable {
@inlinable
public __consuming func throttle(
for interval: TimeInterval,
@@ -19,7 +19,7 @@ extension AsyncSequence {
}
}
-public struct AsyncThrottleSequence {
+public struct AsyncThrottleSequence where Base.Element: Sendable {
@usableFromInline
let base: Base
@@ -41,7 +41,7 @@ public struct AsyncThrottleSequence {
}
}
-extension AsyncThrottleSequence: AsyncSequence {
+extension AsyncThrottleSequence: AsyncSequence where Base.Element: Sendable {
public typealias Element = Base.Element
/// The type of iterator that produces elements of the sequence.
@@ -131,7 +131,7 @@ extension AsyncThrottleSequence: AsyncSequence {
}
@usableFromInline
- struct Throttle {
+ struct Throttle: @unchecked Sendable {
private var baseIterator: Base.AsyncIterator
private let actor: ThrottleActor
@@ -164,14 +164,14 @@ extension AsyncThrottleSequence: AsyncSequence {
@inlinable
public __consuming func makeAsyncIterator() -> AsyncStream.Iterator {
return AsyncStream { (continuation: AsyncStream.Continuation) in
+ var throttle = Throttle(
+ baseIterator: base.makeAsyncIterator(),
+ continuation: continuation,
+ interval: interval,
+ scheduler: scheduler,
+ latest: latest
+ )
Task {
- var throttle = Throttle(
- baseIterator: base.makeAsyncIterator(),
- continuation: continuation,
- interval: interval,
- scheduler: scheduler,
- latest: latest
- )
await throttle.start()
}
}.makeAsyncIterator()
diff --git a/Sources/AsyncTimeSequences/Timeout/AsyncTimeoutSequence.swift b/Sources/AsyncTimeSequences/Timeout/AsyncTimeoutSequence.swift
index 2c5b29a..1a7bea6 100644
--- a/Sources/AsyncTimeSequences/Timeout/AsyncTimeoutSequence.swift
+++ b/Sources/AsyncTimeSequences/Timeout/AsyncTimeoutSequence.swift
@@ -8,7 +8,7 @@
import Combine
import Foundation
-public struct AsyncTimeoutSequence {
+public struct AsyncTimeoutSequence where Base.Element: Sendable {
@usableFromInline
let base: Base
@@ -26,7 +26,7 @@ public struct AsyncTimeoutSequence {
}
}
-extension AsyncSequence {
+extension AsyncSequence where Element: Sendable {
@inlinable
public __consuming func timeout(
for interval: TimeInterval,
@@ -41,7 +41,7 @@ public enum AsyncTimeSequenceError: Error {
}
// TODO: handle continuation.finish being called multiple times
-extension AsyncTimeoutSequence: AsyncSequence {
+extension AsyncTimeoutSequence: AsyncSequence where Base.Element: Sendable {
public typealias Element = Base.Element
/// The type of iterator that produces elements of the sequence.
@@ -105,7 +105,7 @@ extension AsyncTimeoutSequence: AsyncSequence {
}
@usableFromInline
- struct Timeout {
+ struct Timeout: @unchecked Sendable {
private var baseIterator: Base.AsyncIterator
private let actor: TimeoutActor
@@ -138,13 +138,13 @@ extension AsyncTimeoutSequence: AsyncSequence {
public __consuming func makeAsyncIterator() -> AsyncThrowingStream.Iterator {
return AsyncThrowingStream {
(continuation: AsyncThrowingStream.Continuation) in
+ var timeout = Timeout(
+ baseIterator: base.makeAsyncIterator(),
+ continuation: continuation,
+ interval: interval,
+ scheduler: scheduler
+ )
Task {
- var timeout = Timeout(
- baseIterator: base.makeAsyncIterator(),
- continuation: continuation,
- interval: interval,
- scheduler: scheduler
- )
await timeout.start()
}
}.makeAsyncIterator()
diff --git a/Sources/AsyncTimeSequencesSupport/ControlledDataSequence.swift b/Sources/AsyncTimeSequencesSupport/ControlledDataSequence.swift
index 6642aa2..d953d4f 100644
--- a/Sources/AsyncTimeSequencesSupport/ControlledDataSequence.swift
+++ b/Sources/AsyncTimeSequencesSupport/ControlledDataSequence.swift
@@ -9,7 +9,7 @@ import Foundation
/// This is a really convenient sequence designed to ease the testing of async sequences.
/// It provides access to the ControlledDataIterator, which is critical for testing.
-public struct ControlledDataSequence: AsyncSequence {
+public struct ControlledDataSequence: AsyncSequence {
public typealias Element = T
public let iterator: ControlledDataIterator
@@ -26,7 +26,7 @@ public struct ControlledDataSequence: AsyncSequence {
/// This class extends AsyncIteratorProtocol in order to provide an object that returns
/// elements on next(). The critical function in this class is waitForItemsToBeSent(count),
/// which allows the owner of this iterator to wait until n elements have been dispatched via next().
-public final class ControlledDataIterator: AsyncIteratorProtocol {
+public final class ControlledDataIterator: AsyncIteratorProtocol {
private let dataActor: ControlledDataActor
init(items: [T]) {
@@ -42,7 +42,7 @@ public final class ControlledDataIterator: AsyncIteratorProtocol {
}
}
-actor ControlledDataActor {
+actor ControlledDataActor {
let items: [T]
var allowedItemsToBeSentCount = Int.zero
var index = Int.zero
diff --git a/SwiftFormatConfiguration.json b/SwiftFormatConfiguration.json
deleted file mode 100644
index d6854ba..0000000
--- a/SwiftFormatConfiguration.json
+++ /dev/null
@@ -1,53 +0,0 @@
-{
- "blankLineBetweenMembers" : {
- "ignoreSingleLineProperties" : true
- },
- "indentation" : {
- "spaces" : 2
- },
- "indentConditionalCompilationBlocks" : true,
- "lineBreakBeforeControlFlowKeywords" : false,
- "lineBreakBeforeEachArgument" : false,
- "lineLength" : 100,
- "maximumBlankLines" : 1,
- "respectsExistingLineBreaks" : true,
- "rules" : {
- "AllPublicDeclarationsHaveDocumentation" : true,
- "AlwaysUseLowerCamelCase" : true,
- "AmbiguousTrailingClosureOverload" : true,
- "BeginDocumentationCommentWithOneLineSummary" : true,
- "BlankLineBetweenMembers" : true,
- "CaseIndentLevelEqualsSwitch" : true,
- "DoNotUseSemicolons" : true,
- "DontRepeatTypeInStaticProperties" : true,
- "FullyIndirectEnum" : true,
- "GroupNumericLiterals" : true,
- "IdentifiersMustBeASCII" : true,
- "MultiLineTrailingCommas" : true,
- "NeverForceUnwrap" : true,
- "NeverUseForceTry" : true,
- "NeverUseImplicitlyUnwrappedOptionals" : true,
- "NoAccessLevelOnExtensionDeclaration" : true,
- "NoBlockComments" : true,
- "NoCasesWithOnlyFallthrough" : true,
- "NoEmptyTrailingClosureParentheses" : true,
- "NoLabelsInCasePatterns" : true,
- "NoLeadingUnderscores" : true,
- "NoParensAroundConditions" : true,
- "NoVoidReturnOnFunctionSignature" : true,
- "OneCasePerLine" : true,
- "OneVariableDeclarationPerLine" : true,
- "OnlyOneTrailingClosureArgument" : true,
- "OrderedImports" : true,
- "ReturnVoidInsteadOfEmptyTuple" : true,
- "UseEnumForNamespacing" : true,
- "UseLetInEveryBoundCaseVariable" : true,
- "UseShorthandTypeNames" : true,
- "UseSingleLinePropertyGetter" : true,
- "UseSynthesizedInitializer" : true,
- "UseTripleSlashForDocumentationComments" : true,
- "ValidateDocumentationComments" : true
- },
- "tabWidth" : 8,
- "version" : 1
-}
diff --git a/Tests/AsyncTimeSequencesTests/AsyncDebounceSequence+Tests.swift b/Tests/AsyncTimeSequencesTests/AsyncDebounceSequence+Tests.swift
index 9ae6701..483967c 100644
--- a/Tests/AsyncTimeSequencesTests/AsyncDebounceSequence+Tests.swift
+++ b/Tests/AsyncTimeSequencesTests/AsyncDebounceSequence+Tests.swift
@@ -16,7 +16,7 @@ final class AsyncDebounceSequence_Tests: XCTestCase {
func testAsyncDebounceSequence() async {
// Given
let scheduler = TestAsyncScheduler()
- let items = [1, 5, 10, 15, 20]
+ let items: [Int] = [1, 5, 10, 15, 20]
let expectedItems = [20]
let baseDelay = 5.0
var receivedItems = [Int]()
diff --git a/Tests/AsyncTimeSequencesTests/AsyncSchedulerTests.swift b/Tests/AsyncTimeSequencesTests/AsyncSchedulerTests.swift
index 8bf6b79..f7a17d1 100644
--- a/Tests/AsyncTimeSequencesTests/AsyncSchedulerTests.swift
+++ b/Tests/AsyncTimeSequencesTests/AsyncSchedulerTests.swift
@@ -11,6 +11,18 @@ import XCTest
final class AsyncSchedulerTests: XCTestCase {
+ private actor SafeCounter {
+ private var setCounter = Set()
+
+ func insert(_ value: Int) {
+ setCounter.insert(value)
+ }
+
+ func retrieveSet() -> Set {
+ return setCounter
+ }
+ }
+
func testMainAsyncSchedulerSortsScheduledClosures() async {
// Given
let scheduler = MainAsyncScheduler()
@@ -32,24 +44,24 @@ final class AsyncSchedulerTests: XCTestCase {
let scheduler = MainAsyncScheduler()
let firstExpectation = XCTestExpectation(description: "First Expectation")
let secondExpectation = XCTestExpectation(description: "Second Expectation")
- var setCounter = Set()
+ let safeCounter = SafeCounter()
// When
// schedule after 100us
await scheduler.schedule(after: 0.0001) {
- setCounter.insert(1)
+ await safeCounter.insert(1)
firstExpectation.fulfill()
}
// schedule after 15 ms
let cancellableTask = await scheduler.schedule(after: 0.030) {
- setCounter.insert(2)
+ await safeCounter.insert(2)
XCTFail("This should be triggered once cancelled")
}
// Without cancelling the task, it would execute and fail
cancellableTask.cancel()
// schedule after 100us
await scheduler.schedule(after: 0.0001) {
- setCounter.insert(3)
+ await safeCounter.insert(3)
secondExpectation.fulfill()
}
@@ -62,6 +74,7 @@ final class AsyncSchedulerTests: XCTestCase {
let allItemsCompleted = await scheduler.areAllScheduledItemsCompleted()
// Then
+ let setCounter = await safeCounter.retrieveSet()
XCTAssertFalse(setCounter.contains(2))
XCTAssertEqual(setCounter, Set([1, 3]))
XCTAssertTrue(isQueueEmpty)
@@ -101,7 +114,7 @@ final class AsyncSchedulerTests: XCTestCase {
let timeInterval: TimeInterval = 0.0001 // 100us
let safeActorArray = SafeActorArrayWrapper()
var expectedResult = [Int]()
- let maxElements = 100
+ let maxElements = 1000
// When
for index in 0.. {
+actor SafeActorArrayWrapper {
private var _elements = [T]()
private var savedCount = 0
private var savedContinuation: CheckedContinuation?