Futures with Combine

A future is a tool for handling concurrency in a type-safe way. It's a wrapper around a primitive type like an Int that allows the programmer to essentially say, "this will be an Int, some time in the future". Often, the word "promise" is used interchangeably with "future" (as in "I promise this will be an Int eventually"), although there are some semantic differences I won't discuss here.

In Swift, we can define a Future like so:

let future = Future<Int, Never> { callback in
  DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
    callback(.success(10))
  }
}

Notice that the type signature here is Future<Int, Never>. The Never implies that this Future will always succeed, but often this is used for error handling (e.g. Future<Response, Error>).

We can get the value of this Future by subscribing to it (this isn't necessarily the case in other frameworks, but Combine is built on the idea of publishers and subscribers. Publishers emit values, subscribers listen to those values.

There are a few different ways to create a subscriber, but the simplest is to call sink() on the future, which returns an AnyCancellable. More features are available by creating an AnySubscriber instead.

let cancellable = future.sink { number in
  print(number)
} 

One caveat to Future in Combine is that the code in block is executed eagerly, as soon as the future is defined, even if no object is subscribed. However, this behavior can be changed if we add the Deferred wrapper around the definition.

let future = Deferred {
	Future<Int, Never> { callback in
	  DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
	    callback(.success(10))
	  }
	}
}

References