Conbini provides convenience Publisher
s, operators, and Subscriber
s to squeeze the most out of Apple's Combine framework.
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.let publisher = setConfigurationOnServer.then { subscribeToWebsocket.publisher }
-
Complete
never emits a value and just completes (whether successfully or with a failure). It offers similar functionality toEmpty
andFailure
, 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
Output
and/orFailure
toNever
if 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:-
DeferredValue
emits 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() }
-
DeferredResult
offers the same functionality asDeferredValue
, but the closure generates aResult
instead.let publisher = DeferredResult { guard someExpression else { return .failure(CustomError()) } return .success(someValue) }
-
DeferredCompletion
offers the same functionality asDeferredValue
, but the closure only generates a completion event.let publisher = DeferredCompletion { try somethingThatMightFail() }
-
DeferredPassthrough
is similar to wrapping aPassthrough
subject on aDeferred
closure, with the diferrence that thePassthrough
given 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
:Future
publishers 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.Deferred
is the most similar in functionality, but it only accepts a publisher.
-
-
Then
provides the functionality of thethen
operator.
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.
-
expectsCompletion
subscribes to a publisher making the running test wait for a successful completion while ignoring all emitted values.publisherChain.expectsCompletion(timeout: 0.8, on: test)
-
expectsFailure
subscribes to a publisher making the running test wait for a failed completion while ignoring all emitted values.publisherChain.expectsFailure(timeout: 0.8, on: test)
-
expectsOne
subscribes 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)
-
expectsAll
subscribes 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)
-
expectAtLeast
subscribes 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
Combine
framework and the helpful Japanese convenience stores 😄