Future
Combine provides the Future publisher class to model a single asynchronous event. The Future acts like a promise. It can either fulfil its promise or fail to fulfil its promise. Those familiar with promises from frameworks like PromiseKit might liken Future to a promise.
The added benefit of using Future is that it is a publisher, so you can use them with operators and subscribers and easily integrate them into combine data streams.
A Future emits a single event
Apple’s documentation states that Future is:
A publisher that eventually produces a single value and then finishes or fails.
In a marble diagram, a single event followed by a completion or an error would look like this.

Create a Future
Let’s create a simple Future that waits 2 seconds and emits a random number.
Future is generic over an associated type of output and failure. In this example we will use <Int, Never> to emit an integer and never fail the stream.
The Future initialiser takes a closure that takes another closure, (Result<Int, Never>) -> Void, as its input. We will call this the promise. It is a promise to return a result that matches the generic types of the Future. We hold onto this promise in our Future closure and then call the promise with our result or error whenever we have finished our work.
Simple right?

It makes more sense in code. It looks like this.
Future<Int, Never> { promise in
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
let number = Int.random(in: 1...10)
promise(.success(number))
}
}
If we wanted to emit the failure event we simply call the promise like so
Future<Int, Never> { promise in
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
let number = Int.random(in: 1...10)
promise(.failure(.someErrorCase))
}
}
As you can see the Future initialiser provides you with a promise that you then call with a value or an error to emit something from the Future publisher.
When does Future process?
If we change the future publisher back to emitting a success and include some print statements we can look into how and when the future is processing its closures.
let futurePublisher = Future<Int, Never> { promise in
print("๐ฎ Future began processing")
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
let number = Int.random(in: 1...10)
print("๐ฎ Future emitted number: \(number)")
promise(Result.success(number))
}
}.print("๐ Publisher event")
Compiling and running this code would create the following logs.
๐ฎ Future began processing
๐ฎ Future emitted number: 2
As you can see, when we create a Future it immediately starts processing.
There are no logs from .print("๐ Publisher event"). No downstream subscribers would receive events. This is because there are no subscribers. Even though the Future is doing its work.
Next, we will add a simple subscriber using sink and print the received value.
let futurePublisher = Future<Int, Never> { promise in
print("๐ฎ Future began processing")
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
let number = Int.random(in: 1...10)
print("๐ฎ Future emitted number: \(number)")
promise(Result.success(number))
}
}.print("๐ Publisher event")
futurePublisher
.sink { print ("๐ง Future stream received: \($0)") }
.store(in: &cancellables)
Now the logs look like this.
๐ฎ Future began processing
๐ Publisher event: receive subscription: (Future)
๐ Publisher event: request unlimited
๐ฎ Future emitted number: 3
๐ Publisher event: receive value: (3)
๐ง Future stream received: 3
๐ Publisher event: receive finished
The downstream subscriber clearly receives the emitted event.
API Call Example with Future
Because Future models a one-time asynchronous event that succeeds or fails it is often used to model an API call or network fetch. Here is how that might look.
func fetch(url: URL) -> AnyPublisher<[Post], Error> {
Future { promise in
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
promise(.failure(error))
return
}
do {
let posts = try JSONDecoder().decode([Post].self, from: data!)
promise(.success(posts))
} catch {
promise(.failure(error))
}
}.resume()
}.eraseToAnyPublisher()
}
fetch(url: url)
.sink(receiveCompletion: { completion in
print(completion)
}, receiveValue: { value in
print(value)
}).store(in: &cancellables)
This code leverages the traditional completion handler of a URLSessionDataTask to create a Future. The Future object is then type erased to an AnyPublisher for easy use with any downstream subscribers.
Remember that simply calling the fetch(url:) function will initiate an API call. Therefore, if you are using a function like fetch(url:) and not immediately subscribing to it, you may want to defer any work that the Future might do until something subscribes.
Deferred
The Deferred struct is a simple publisher that takes in a closure as a parameter. That closure must return a DeferredPublisher. It will call this closure only when a subscriber subscribes. In this way, it defers any publisher processing until a subscriber causes downstream demand for an event.
Deferred with one subscriber
When we looked at when a future processes we saw that the Future began its processing as soon as it was created. If we wrap the same code with Deferred we can see how this defers any processing until a subscription happens.
let deferredPublisher = Deferred {
Deferred {
Future<Int, Never> { promise in
print("๐ฎ Future began processing")
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
let number = Int.random(in: 1...10)
print("๐ฎ Future emitted number: \(number)")
promise(Result.success(number))
}
}.print("๐ Publisher event")
}
}
This prints nothing to the console as nothing is subscribed. Deferred won’t even create the Future until there is a subscription.

Let’s add a subscriber.
let deferredPublisher = Deferred {
Deferred {
Future<Int, Never> { promise in
print("๐ฎ Future began processing")
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
let number = Int.random(in: 1...10)
print("๐ฎ Future emitted number: \(number)")
promise(Result.success(number))
}
}.print("๐ Publisher event")
}
}
deferredPublisher
.sink { print ("๐ง Future stream received: \($0)") }
.store(in: &cancellables)
Now we get the same logs as when we created the Future without Deferred, but only because we have a definitive subscriber.
๐ฎ Future began processing
๐ Publisher event: receive subscription: (Future)
๐ Publisher event: request unlimited
๐ฎ Future emitted number: 7
๐ Publisher event: receive value: (7)
๐ง Future stream received: 7
๐ Publisher event: receive finished
Deferred with multiple subscribers
Let’s subscribe two subscribers to the same deferred publisher.
deferredPublisher
.sink { print ("๐ง Future stream 1 received: \($0)") }
.store(in: &cancellables)
deferredPublisher
.sink { print ("๐ง Future stream 2 received: \($0)") }
.store(in: &cancellables)
Here are the logs we get now.
๐ฎ Future began processing
๐ Publisher event: receive subscription: (Future)
๐ Publisher event: request unlimited
๐ฎ Future began processing
๐ Publisher event: receive subscription: (Future)
๐ Publisher event: request unlimited
๐ฎ Future emitted number: 9
๐ Publisher event: receive value: (9)
๐ง Future stream 1 received: 9
๐ Publisher event: receive finished
๐ฎ Future emitted number: 6
๐ Publisher event: receive value: (6)
๐ง Future stream 2 received: 6
๐ Publisher event: receive finished
Notice that the emitted events are different.
Each subscription makes the DeferredPublisher run its closure that creates a Future. Therefore each subscription gets a different Future and therefore a different emitted event.
Conclusion
Future acts like a promise to return some value in the future. It can also fail. It’s useful for creating publishers of one-time asynchronous events. We need to be conscious that Future does its processing as soon as it is created.
Deferred wraps the creation of any type of publisher and only creates the publisher when a subscriber is present. Used with Future deferred can defer the Future’s work until downstream subscribers are present.