It will take about 10 minutes to finish reading this article.
Overview
In the previous section, we introduced the Publisher and Subscriber types along with some of the types under the Publishers and Subscribers namespaces in the Combine framework. These two types are the core concepts of the Combine framework. This article will continue to introduce some other common types in the Combine framework.
1. AnyPublisher
AnyPublisher is the type-erased version of the Publisher type. It hides the specific underlying publisher type and exposes a generic interface for handling publishers.
Example Code One:
1 | let specificPublisher = Just("Hello, Combine!") |
You can create an AnyPublisher from any publisher using the eraseToAnyPublisher() method. You can subscribe to it, apply operators, and handle the values and errors it emits just like any other Publisher. It’s especially useful when you want to combine or merge multiple publishers of different types into a single type-erased publisher for further processing.
Example Code Two:
AnyPublisher is particularly useful when you want to combine or merge multiple publishers of different types into a single type-erased publisher for further processing:
1 | let publisher1 = ... |
2. Publishered
Published
is a property wrapper in SwiftUI based on Combine, used to create observable object properties. It converts property changes into Combine publishers, enabling the interface to update automatically to reflect the changes. This is useful for creating responsive user interfaces.
Example Code:
1 | class Weather { |
3. Cancellable and AnyCancellable
3.1 Cancellable
Cancellable
is a protocol that represents an object that can be canceled for a subscription. It allows you to manually cancel subscriptions when they are no longer needed to avoid unnecessary resource consumption or the continued propagation of data streams.
1 | protocol Cancellable |
Calling cancel() not only frees allocated resources, it may also have side effects such as stopping timers, network access, or disk I/O.
1 |
|
3.2 AnyCancellable
AnyCancellable
is a type-erased object for Cancellable
that manages and holds one or more Cancellable
objects. An instance of AnyCancellable
automatically calls cancel() upon deallocation, ensuring that they are properly canceled when they are no longer needed.
1 | import Combine |
4. Future
Future
is a type of Publisher that represents an asynchronous operation that may produce a result in the future. It can be used to create a Publisher that will produce a value or an error at some point in the future.
1 |
|
Example Code:
1 | let futureExample = Future<Int, Error> { promise in |
Futures are often used in Combine to handle one-time asynchronous operations, such as network requests or computationally intensive tasks. It allows these operations to be wrapped into Publishers so that they can be combined, transformed and processed with other data streams.
5. Just
Just
is a type of Publisher that is used to create a Publisher that emits a single specified value. Unlike Future
, Just
immediately emits its value as soon as it is created, rather than executing an asynchronous operation at some future point.
Example Code:
1 | let justExample = Just("Hello, Combine!") |
Just Publisher is useful for creating data flows for testing or serving static data, as it can easily convert a specific value into a Publisher. Note, however, that it is not suitable for representing dynamic or asynchronous data flows. For the latter, you might consider using a Future or other appropriate Publisher type.
6. Deferred
Deferred
is a type of Publisher used to delay the creation and subscription of another Publisher. Unlike other Publishers, Deferred
executes its closure when it’s subscribed to, which is used to create and return the actual Publisher you want to subscribe to.
Example Code:
1 | let deferredExample = Deferred { |
Initialize with a closure that will be executed every time you subscribe, and the return value of the closure is the actual publisher you want to subscribe to.
1 | deferredExample |
You can subscribe to a Deferred publisher just like other Combine publishers and process the values it emits.
Note: The closure of Deferred will only be executed when subscribing, and it will be executed once for each subscription. This means that the actual publisher is created lazily and can be dynamically generated as needed.
Deferreds are a type of conditional Publisher: you can use Deferreds to create Publishers that are generated based on certain conditions, which allows you to generate different data streams when needed to adapt to different situations. Deferred is a very useful publisher type that allows you to generate publishers on demand and perform related logic when subscribing. This is useful for implementing dynamic, conditional, or context-based data flow in Combine flows.
7. Empty
Empty is also a publisher type, which represents a publisher that does not emit any value and will only be completed immediately. It is usually used to represent an empty data stream or a data stream that contains no data.
1 | struct Empty<Output, Failure> where Failure : Error |
Example Code:
1 | let emptyExample = Empty<Int, Never>() |
You can subscribe to the Empty publisher just like other Combine publishers, but since it completes immediately and emits no value, you usually only need to handle the completion status.
1 | emptyExample |
Empty
Publisher is commonly used to represent situations where no data is available under certain conditions in the Combine data stream. It can be used to create an empty data stream for combining and processing with other Publishers.
8. Fail
Fail
is a type of Publisher that represents a Publisher that will immediately complete with a specified error. Unlike Empty
, Fail
emits an error without emitting any values. The error type is a subtype of Error
.
Example Code:
1 | let failExample = Fail<Int, MyError>(error: MyError.someError) |
Since it completes immediately and issues an error, you usually only need to handle the error.
Fail is usually used to simulate certain failure or error situations in order to test error handling logic in the Combine data flow. It can also be used to indicate that under certain conditions the data stream is unable to provide valid data and an error occurs.
9. Record
Record
is also a type of Publisher that allows recording a series of input and completion events for later playback to each subscriber. You can create a Record
Publisher using different initialization methods.
1. Create a Record publisher with the provided output and completion events:
1 | let values: [Int] = [1, 2, 3] |
2. Use closures to interactively log output and completion events:
1 | let recordPublisher = Record<Int, MyError> { recorder in |
3. Create a Record publisher using existing records (Recording):
1 | let existingRecording: Record<Int, MyError>.Recording = ... |
You can subscribe to Record
Publisher like any other Combine Publisher. It will replay the previously recorded values and completion events to the subscriber.
1 | recordPublisher |
Record is a powerful tool that can be used to record and playback data streams for testing, simulation, and other situations where data needs to be replayed at different points in time. This can help simplify and improve testing and debugging of code.
10. ConnectablePublisher
ConnectablePublisher
is a special type of Publisher that requires manually calling the connect()
method to start publishing values. Unlike regular Publishers, a ConnectablePublisher
doesn’t immediately start propagating data when subscribed to. Instead, it waits until the connect()
method is called.
Example Code:
1 | let publisher = [1, 2, 3].publisher.makeConnectable() |
Once connected, the ConnectablePublisher
will continue to publish data until you disconnect. You can stop receiving data by canceling the subscription or automatically connect until there are no subscribers by using the autoconnect()
method.
1 | cancelable.cancel() // Stop receiving data |
ConnectablePublisher
is useful for scenarios where multiple subscribers need to operate on the same data stream. By manually connecting, you can ensure that subscribers start receiving data only when they are ready to receive, meeting specific requirements. This approach can improve the performance and efficiency of your code.