Combine Framework In Swift -2

Keerthiraj Su
3 min readJun 16, 2021

Next we will look at Subjects and Future in Combine

A subject is a publisher that you can use to “inject” values into a stream, by calling its send(_:) method. This can be useful for adapting existing imperative code to the Combine model.

Combine ships with two subject implementations: PassthroughSubject and CurrentValueSubject

PassthroughSubject doesn’t have an initial value or a buffer of the most recently-published element.

let subject = PassthroughSubject<Int,Never>()
let firstSubscriber = subject
.sink(receiveValue: {
print($0)
})
subject.send(33)

Output: 33

A PassthroughSubject drops values if there are no subscribers, or its current demand is zero. send(33)Sends a value to the subscriber.

Unlike PassthroughSubject, CurrentValueSubject maintains a buffer of the most recently published element.

let anotherSub = CurrentValueSubject<String,Never>("Hello Sir")
let secondSubscriber = anotherSub
.sink(receiveValue: {
print ($0)
})
anotherSub.send("How are you")

In Above example we have a initial Value “Hello sir” then send(“How are you”) will get append to the earlier Value and output will get updated

Output: Hello Sir
How are you

However, one thing to keep in mind when using PassthroughSubject is that each subscriber that attaches to it will only receive the values that were sent after that subscription became active.

func manySubscribersSingleSubject() {
let subject = PassthroughSubject<String, Never>()
let publisher = subject.eraseToAnyPublisher()
let subscriber1 = publisher.sink(receiveValue: { value in
print(value)})
//subscriber1 will receive the text but not the subscriber2
subject.send(“Cricket”)
subject.send(“Football”)

let subscriber2 = publisher.sink(receiveValue: { value in print(value)})
//Subscriber1 and Subscriber2 will receive this text
subject.send("Tennis")}

Future

A publisher that eventually produces a single value and then finishes or fails.

Combine Future gives us a promise closure that we can call when an asynchronous operation was completed, and Combine will then automatically map the Result that we give that closure into proper publisher events.

func performAsyncAction(completionHandler: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline:.now() + 2) {
completionHandler()
}
}

In the above example we are returning a Completionhandler which we will be handled by the Source class also handle success and failure

You can replace this pattern with a Combine Future, a publisher that performs some work and then asynchronously signals success or failure. If it succeeds, the future executes a Future.Promise, a closure that receives the element produced by the future. You can replace the previous function as follows:

func performAsyncActionAsFuture() -> Future <Void, Never> {
return Future() { promise in
DispatchQueue.main.asyncAfter(deadline:.now() + 2) {
promise(Result.success(()))
}
}
}

Later it can be handled as

cancellable = performAsyncActionAsFuture()
.sink() { _ in print("Future succeeded.") }

Lets take one practical example

func fetchImage(forURL url: String, completion: @escaping (Result<Data, Error>) -> ()) {
let url = URL(string: url)!
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { (data, _, error) in
if (error == nil),let data = data {
completion(.success(data))
} else {
completion(.failure(NetworkError.genericError))
}
}.resume()
}

This can be changed to Future publisher so that operators can be applied on the data

func fetchImage(forURL url: String) -> Future<Data, Error> {
let url = URL(string: url)!
let request = URLRequest(url: url)
return Future<Data, Error> { promise in
URLSession.shared.dataTask(with: request) { (data, _, error) in
if (error == nil),let data = data {
promise(.success(data))
} else {
promise(.failure(NetworkError.genericError))
}
}.resume()
}
}

Keep Coding. Keep Improving !!

--

--