Skip to content

Commit

Permalink
Result operator added
Browse files Browse the repository at this point in the history
  • Loading branch information
dehesa committed Oct 28, 2019
1 parent 46ee97a commit 5525bc8
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 5 deletions.
29 changes: 25 additions & 4 deletions README.md
Expand Up @@ -10,12 +10,25 @@ Conbini provides convenience `Publisher`s, operators, and `Subscriber`s to squee

- `then` ignores all values and executes the provided publisher once a successful completion is received.
If a failed completion is emitted, it is forwarded downstream.

```swift
let publisher = setConfigurationOnServer.then {
subscribeToWebsocket.publisher
}
```

- `result` subscribes to the receiving publisher and execute the handler when a single value followed by a successful completion is received.
In case of failure, the handler is executed with such failure.

```swift
let cancellable = serverRequest.result { (result) in
switch result {
case .success(let value): ...
case .failure(let error): ...
}
}
```

## Publishers

- `Complete` never emits a value and just completes (whether successfully or with a failure).
Expand Down Expand Up @@ -70,9 +83,9 @@ Conbini provides convenience `Publisher`s, operators, and `Subscriber`s to squee

- `Then` provides the functionality of the `then` operator.

## Subscribers
# Testing

The following operators are actually testing subscribers. They allow easier testing for publisher chains making the test wait till a specific expectation is fulfilled (or making the test fail in a negative case). Furthermore, if a timeout ellapses or a expectation is not fulfilled, the affected test line will be marked _in red_ correctly in Xcode.
Conbini provides convenience subscribers to ease code testing. These subscribers make the test wait till a specific expectation is fulfilled (or making the test fail in a negative case). Furthermore, if a timeout ellapses or a expectation is not fulfilled, the affected test line will be marked _in red_ correctly in Xcode.

- `expectsCompletion` subscribes to a publisher making the running test wait for a successful completion while ignoring all emitted values.

Expand Down Expand Up @@ -114,12 +127,20 @@ The following operators are actually testing subscribers. They allow easier test
}
```

## References
## Quirks

Conbini testing conveniences depend on [XCTest](https://developer.apple.com/documentation/xctest), which is not available on regular execution. That is why Conbini is offered in two flavors:

- `import Conbini` imports all code excepts the testing conveniences.
- `import ConbiniForTesting` imports everything.

The rule of thumb is to use `import Conbini` in your regular code (e.g. within your framework or app) and write `import ConbiniForTesting` within your test target files.

# References

- Apple's [Combine documentation](https://developer.apple.com/documentation/combine).
- [OpenCombine](https://github.com/broadwaylamb/OpenCombine) is an open source implementation of Apple's Combine framework.
- [CombineX](https://github.com/cx-org/CombineX) is an open source implementation of Apple's Combine framework.
- [SwiftUI-Notes](https://heckj.github.io/swiftui-notes/) is a collection of notes on Swift UI and Combine.
- [Combine book](https://store.raywenderlich.com/products/combine-asynchronous-programming-with-swift) is an excellent Ray Wenderlich book about the Combine framework.

> The framework name references both the `Combine` framework and the helpful Japanese convenience stores 😄
38 changes: 38 additions & 0 deletions Sources/Conbini/Operators/Result.swift
@@ -0,0 +1,38 @@
import Combine
import Foundation

extension Publisher {
/// This operator creates a subscribers (such as the `.sink` operator) which subscribes upstream and expects a single value and a subsequent successful completion.
/// - If a single value is sent, followed by a successful completion; the handler is called with such value.
/// - If a failure occurs at any point; then handler is called with such failure.
/// - If more than one value is sent; the subscriber gets cancelled and the handler is never called.
/// - If a completion occurs and no value has been sent, the subscriber completes without calling the handler.
/// - parameter handler: Returns the result of the publisher.
@discardableResult
public func result(_ handler: @escaping (Result<Output,Failure>)->Void) -> AnyCancellable? {
var result: Output? = nil
var cancellable: AnyCancellable? = nil

cancellable = self.sink(receiveCompletion: {
switch $0 {
case .failure(let error):
handler(.failure(error))
case .finished:
if let value = result {
handler(.success(value))
}
}

(result, cancellable) = (nil, nil)
}, receiveValue: {
guard case .none = result else {
cancellable?.cancel()
(result, cancellable) = (nil, nil)
return
}
result = $0
})

return cancellable
}
}
2 changes: 1 addition & 1 deletion Sources/Testing/Subscribers/XCTestCase.swift
Expand Up @@ -6,7 +6,7 @@ import Foundation
extension XCTestCase {
/// Locks the receiving test for `interval` seconds.
/// - parameter interval: The number of seconds waiting (must be greater than zero).
public func wait(_ interval: TimeInterval) {
public func wait(seconds interval: TimeInterval) {
precondition(interval > 0)

let e = self.expectation(description: "Waiting for \(interval) seconds")
Expand Down

0 comments on commit 5525bc8

Please sign in to comment.