Skip to content

Commit

Permalink
Add an Empty sequence (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
reddavis authored Feb 25, 2022
1 parent a05ac17 commit f4d01f4
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 2 deletions.
12 changes: 10 additions & 2 deletions Asynchrone.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@
A45E0F742781B978006B64E1 /* ReplaceErrorAsyncSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45E0F732781B978006B64E1 /* ReplaceErrorAsyncSequence.swift */; };
A45E0F762781BDB1006B64E1 /* ReplaceErrorAsyncSequenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45E0F752781BDB1006B64E1 /* ReplaceErrorAsyncSequenceTests.swift */; };
A45E0F782781C8C4006B64E1 /* TestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A45E0F772781C8C4006B64E1 /* TestError.swift */; };
A47BE65B27C75FE60011ECE6 /* Empty.swift in Sources */ = {isa = PBXBuildFile; fileRef = A47BE65A27C75FE60011ECE6 /* Empty.swift */; };
A47BE65D27C78D2D0011ECE6 /* EmptyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A47BE65C27C78D2D0011ECE6 /* EmptyTests.swift */; };
A47BE66727C8FDC20011ECE6 /* Asynchrone.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4C361AA276A5EF200511525 /* Asynchrone.framework */; };
A47BE66127C7D9280011ECE6 /* DelayAsyncSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = A47BE66027C7D9280011ECE6 /* DelayAsyncSequence.swift */; };
A47BE66327C7E6CD0011ECE6 /* DelayAsyncSequenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A47BE66227C7E6CD0011ECE6 /* DelayAsyncSequenceTests.swift */; };
A47BE66627C8FA0A0011ECE6 /* Asynchrone.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4C361AA276A5EF200511525 /* Asynchrone.framework */; };
A4C361BB276A5EF200511525 /* Asynchrone.h in Headers */ = {isa = PBXBuildFile; fileRef = A4C361AD276A5EF200511525 /* Asynchrone.h */; settings = {ATTRIBUTES = (Public, ); }; };
A4C361C7276A603B00511525 /* AsyncStream+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4C361C6276A603B00511525 /* AsyncStream+Extension.swift */; };
A4C361CF276A68BF00511525 /* RemoveDuplicatesAsyncSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4C361CE276A68BF00511525 /* RemoveDuplicatesAsyncSequence.swift */; };
Expand Down Expand Up @@ -87,6 +89,8 @@
A45E0F732781B978006B64E1 /* ReplaceErrorAsyncSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplaceErrorAsyncSequence.swift; sourceTree = "<group>"; };
A45E0F752781BDB1006B64E1 /* ReplaceErrorAsyncSequenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplaceErrorAsyncSequenceTests.swift; sourceTree = "<group>"; };
A45E0F772781C8C4006B64E1 /* TestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestError.swift; sourceTree = "<group>"; };
A47BE65A27C75FE60011ECE6 /* Empty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Empty.swift; sourceTree = "<group>"; };
A47BE65C27C78D2D0011ECE6 /* EmptyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTests.swift; sourceTree = "<group>"; };
A47BE66027C7D9280011ECE6 /* DelayAsyncSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelayAsyncSequence.swift; sourceTree = "<group>"; };
A47BE66227C7E6CD0011ECE6 /* DelayAsyncSequenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelayAsyncSequenceTests.swift; sourceTree = "<group>"; };
A4C361AA276A5EF200511525 /* Asynchrone.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Asynchrone.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -126,7 +130,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A47BE66627C8FA0A0011ECE6 /* Asynchrone.framework in Frameworks */,
A47BE66727C8FDC20011ECE6 /* Asynchrone.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -142,6 +146,7 @@
A4C361E92770FA7C00511525 /* CombineLatest3AsyncSequenceTests.swift */,
A40B6E4127883FD300CA6502 /* CurrentElementAsyncSequenceTests.swift */,
A45E0F6C2780C0A6006B64E1 /* DebounceAsyncSequenceTests.swift */,
A47BE65C27C78D2D0011ECE6 /* EmptyTests.swift */,
A47BE66227C7E6CD0011ECE6 /* DelayAsyncSequenceTests.swift */,
A45E0EBE27749C07006B64E1 /* FailTests.swift */,
A45E0EBA277491F1006B64E1 /* JustTests.swift */,
Expand Down Expand Up @@ -271,6 +276,7 @@
A4C361E72770F67E00511525 /* CombineLatest3AsyncSequence.swift */,
A40B6E3F2787618800CA6502 /* CurrentElementAsyncSequence.swift */,
A45E0F6A2780AE94006B64E1 /* DebounceAsyncSequence.swift */,
A47BE65A27C75FE60011ECE6 /* Empty.swift */,
A47BE66027C7D9280011ECE6 /* DelayAsyncSequence.swift */,
A45E0EBC27749897006B64E1 /* Fail.swift */,
A45E0EB827748F5A006B64E1 /* Just.swift */,
Expand Down Expand Up @@ -421,6 +427,7 @@
A4C361E82770F67E00511525 /* CombineLatest3AsyncSequence.swift in Sources */,
A40B6E462788885800CA6502 /* PassthroughAsyncSequence.swift in Sources */,
A45E0EB927748F5A006B64E1 /* Just.swift in Sources */,
A47BE65B27C75FE60011ECE6 /* Empty.swift in Sources */,
A4C361F52772319100511525 /* Zip3AsyncSequence.swift in Sources */,
A4C361D7276CE9DC00511525 /* AsyncSequence+Extension.swift in Sources */,
A45E0F742781B978006B64E1 /* ReplaceErrorAsyncSequence.swift in Sources */,
Expand All @@ -445,6 +452,7 @@
buildActionMask = 2147483647;
files = (
A40B6E53278CABEA00CA6502 /* TimeIntervalTests.swift in Sources */,
A47BE65D27C78D2D0011ECE6 /* EmptyTests.swift in Sources */,
A4C361D1276B39F600511525 /* RemoveDuplicatesAsyncSequenceTests.swift in Sources */,
A4C361E62770EE9200511525 /* Merge3AsyncSequenceTests.swift in Sources */,
A40B6E4227883FD300CA6502 /* CurrentElementAsyncSequenceTests.swift in Sources */,
Expand Down
59 changes: 59 additions & 0 deletions Asynchrone/Source/Sequences/Empty.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/// An asynchronous sequence that only emits the provided value once.
///
/// ```swift
/// Empty<Int>().sink(
/// receiveValue: { print($0) },
/// receiveCompletion: { completion in
/// switch completion {
/// case .finished:
/// print("Finished")
/// case .failure:
/// print("Failed")
/// }
/// }
/// )
///
/// // Prints:
/// // Finished
/// ```
public struct Empty<Element>: AsyncSequence {

// Private
private let completeImmediately: Bool

// MARK: Initialization

/// Creates an empty async sequence.
///
/// - Parameter completeImmediately: A Boolean value that indicates whether
/// the async sequence should immediately finish.
public init(completeImmediately: Bool = true) {
self.completeImmediately = completeImmediately
}

// MARK: AsyncSequence

/// Creates an async iterator that emits elements of this async sequence.
/// - Returns: An instance that conforms to `AsyncIteratorProtocol`.
public func makeAsyncIterator() -> Self {
.init(completeImmediately: self.completeImmediately)
}
}

// MARK: AsyncIteratorProtocol

extension Empty: AsyncIteratorProtocol {

/// Produces the next element in the sequence.
///
/// Because this is an empty sequence, this will always be nil.
///
/// - Returns: `nil` as this is an empty sequence.
public mutating func next() async -> Element? {
if !self.completeImmediately {
try? await Task.sleep(seconds: 999_999_999)
}

return nil
}
}
45 changes: 45 additions & 0 deletions AsynchroneTests/Tests/Sequences/EmptyTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import XCTest
@testable import Asynchrone


final class EmptyTests: XCTestCase {
func testNothingEmitted() async {
let values = await Empty<Int>().collect()
XCTAssertEqual(values, [])
}

func testSequenceDoesntComplete() async {
do {
try await withThrowingTaskGroup(of: Void.self) { group in
let timeout: TimeInterval = 1
let deadline = Date(timeIntervalSinceNow: timeout)

group.addTask {
_ = await Empty<Int>(completeImmediately: false).collect()
throw TaskCompletedError()
}

group.addTask {
if deadline.timeIntervalSinceNow > 0 {
try await Task.sleep(seconds: timeout)
}
}

try await group.next()
group.cancelAll()
}

// All good!
} catch _ as TaskCompletedError {
XCTFail("Task incorrectly finished")
} catch {
XCTFail("Unknown error thrown \(error)")
}
}
}



// MARK: TaskCompletedError

fileprivate struct TaskCompletedError: Error { }
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,26 @@ for element in try await self.stream.delay(for: 0.5) {
// 0 - 0.5
// 1 - 1.0
// 2 - 1.5
>>>>>>> main
```

### [Empty](https://distracted-austin-575f34.netlify.app/structs/empty)

```swift
Empty<Int>().sink(
receiveValue: { print($0) },
receiveCompletion: { completion in
switch completion {
case .finished:
print("Finished")
case .failure:
print("Failed")
}
}
)

// Prints:
// Finished
```

### [Fail](https://distracted-austin-575f34.netlify.app/structs/fail)
Expand Down

0 comments on commit f4d01f4

Please sign in to comment.