Conbini provides convenience Publishers, operators, and Subscribers to squeeze the most out of Apple's Combine framework.
thenignores all values and executes the provided publisher once a successful completion is received. If a failed completion is emitted, it is forwarded downstream.let publisher = setConfigurationOnServer.then { subscribeToWebsocket.publisher }
-
Completenever emits a value and just completes (whether successfully or with a failure). It offers similar functionality toEmptyandFailure, but in a single type. Also, the completion only happens once a greater-than-zero demand is requested.let publisherA = Complete<Int,CustomError>(error: nil) let publisherB = Complete(error: CustomError())
There are two more convenience initializers setting the publisher's
Outputand/orFailuretoNeverif the generic types are not explicitly stated. -
Deferred...publishers accept a closure that is executed once a greater-than-zero demand is requested. They have several flavors:-
DeferredValueemits a single value and then completes; however, the value is not provided/cached, but instead a closure which will generate the emitted value is executed per subscription received.let publisher = DeferredValue { return try someHeavyCalculations() }
-
DeferredResultoffers the same functionality asDeferredValue, but the closure generates aResultinstead.let publisher = DeferredResult { guard someExpression else { return .failure(CustomError()) } return .success(someValue) }
-
DeferredCompletionoffers the same functionality asDeferredValue, but the closure only generates a completion event.let publisher = DeferredCompletion { try somethingThatMightFail() }
-
DeferredPassthroughis similar to wrapping aPassthroughsubject on aDeferredclosure, with the diferrence that thePassthroughgiven on the closure is already wired on the publisher chain and can start sending values right away. Also, the memory management is taken care of and every new subscriber receives a new subject (closure re-execution).let publisher = DeferredPassthrough { (subject) in subject.send(something) subject.send(completion: .finished) }
There are several reason for these publishers to exist instead of using other
Combine-provided closure such asJust,Future, orDeferred:Futurepublishers execute their provided closure right away (upon initialization) and then cache the returned value. That value is then forwarded for any future subscription.Deferred...closures await for subscriptions and a greater-than-zero demand before executing. This also means, the closure will re-execute for any new subscription.Deferredis the most similar in functionality, but it only accepts a publisher.
-
-
Thenprovides the functionality of thethenoperator.
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.
-
expectsCompletionsubscribes to a publisher making the running test wait for a successful completion while ignoring all emitted values.publisherChain.expectsCompletion(timeout: 0.8, on: test)
-
expectsFailuresubscribes to a publisher making the running test wait for a failed completion while ignoring all emitted values.publisherChain.expectsFailure(timeout: 0.8, on: test)
-
expectsOnesubscribes to a publisher making the running test wait for a single value and a successful completion. If more than one values are emitted or the publisher fails, the subscription gets cancelled and the test fails.let emittedValue = publisherChain.expectsOne(timeout: 0.8, on: test)
-
expectsAllsubscribes to a publisher making the running test wait for zero or more values and a successful completion.let emittedValues = publisherChain.expectsAll(timeout: 0.8, on: test)
-
expectAtLeastsubscribes to a publisher making the running test wait for at least the provided amount of values. Once the provided amount of values is received, the publisher gets cancelled and the values are returned.let emittedValues = publisherChain.expectsAtLeast(values: 5, timeout: 0.8, on: test)
This operator/subscriber accepts an optional closure to check every value received.
let emittedValues = publisherChain.expectsAtLeast(values: 5, timeout: 0.8, on: test) { (value) in XCTAssert... }
- Apple's Combine documentation.
- OpenCombine is an open source implementation of Apple's Combine framework.
- CombineX is an open source implementation of Apple's Combine framework.
- SwiftUI-Notes is a collection of notes on Swift UI and Combine.
- Combine book is an excellent Ray Wenderlich book about the Combine framework.
The framework name references both the
Combineframework and the helpful Japanese convenience stores 😄