Coordinate when publishers start sending elements to subscribers.
Sometimes, you want to configure a publisher before it starts producing elements, such as when a publisher has properties that affect its behavior. But commonly used subscribers like
sink(receive demand unlimited elements immediately, which might prevent you from setting up the publisher the way you like. A publisher that produces values before you’re ready for them can also be a problem when the publisher has two or more subscribers. This multi-subscriber scenario creates a race condition: the publisher can send elements to the first subscriber before the second even exists.
Consider the scenario in the following figure. You create a
URLSession and attach a sink subscriber to it (Subscriber 1) which causes the data task to start fetching the URL’s data. At some later point, you attach a second subscriber (Subscriber 2). If the data task completes its download before the second subscriber attaches, the second subscriber misses the data and only sees the completion.
Hold Publishing by Using a Connectable Publisher
To prevent a publisher from sending elements before you’re ready, Combine provides the
Connectable protocol. A connectable publisher produces no elements until you call its
connect() method. Even if it’s ready to produce elements and has unsatisfied demand, a connectable publisher doesn’t deliver any elements to subscribers until you explicitly call
The following figure shows the
URLSession scenario from above, but with a
Connectable ahead of the subscribers. By waiting to call
connect() until both subscribers attach, the data task doesn’t start downloading until then. This eliminates the race condition and guarantees both subscribers can receive the data.
To use a
Connectable in your own Combine code, use the
make operator to wrap an existing publisher with a
Publishers instance. The following code shows how
make fixes the data task publisher race condition described above. Typically, attaching a sink — identified here by the
Any it returns,
cancellable1 — would cause the data task to start immediately. In this scenario, the second sink, identified as
cancellable2, doesn’t attach until one second later, and the data task publisher might complete before the second sink attaches. Instead, explicitly using a
Connectable causes the data task to start only after the app calls
connect(), which it does after a two-second delay.
Use the Autoconnect Operator If You Don’t Need to Explicitly Connect
Some Combine publishers already implement
Connectable, such as
Timer. Using these publishers can cause the opposite problem: having to explicitly
connect() could be burdensome if you don’t need to configure the publisher or attach multiple subscribers.
The following example uses
autoconnect(), so a subscriber immediately receives elements from a once-a-second
autoconnect(), the example would need to explicitly start the timer publisher by calling
connect() at some point.