Network Request With Swift Combine

The basics.

Publishers: “Declares that a type can transmit a sequence of values over time.” from the Docs. You can think of Publishers are observables, which publishes/exposes values.

Creating the Publisher.

With the introduction of Combine, Apple provided useful extensions on many APIs in the Foundation framework, let’s create a DataTaskPublisher from the URLSession extension to get a response and manage errors.

URLSession.shared.dataTaskPublisher(for: URL(string: URLPath)!) //1
.map { $0.data } //2
.decode(type: ModelObject.self, //3
decoder: JSONDecoder())
.mapError(mapError) //4
.eraseToAnyPublisher() //5
  1. Gives us a DataTaskPublisher object. It’s a publisher to go and get response from end point and publish a value.
  2. Extract the Data object from response.
  3. Decode Data to a model object of your choice, using JSONDecoder.
  4. Map any error to an appropriate error type of your choice.
  5. This function will provide a publisher with appropriate Output and Error type, for example the return type of this code is AnyPublisher<ModelObject, Error>. AnyPublisher is simply a wrapper object that conforms to the Publisher protocol. ModelObject is the type of object the Publisher publishes and Error is an Error type that was mapped.

Getting the response.

Now that we have a Publisher we need to create a Subscriber to subscribe to our Publisher so we can receive values/updates. The Combine framework gives us the freedom to do this in different ways. You can create your own subscribers or use the Subscribers enum but in this tutorial we are going to look at subscribers provided by our publishers just to keep things simple.

let publisher = networkService.publisher()                       //1     
.receive(on: DispatchQueue.main)
let subscriber = publisher.sink(receiveCompletion:{completion in //2
switch completion {
case .failure(let error):
print(error)
case .finished:
break
}
}) { [weak self] viewModel in
self
?.viewModel = viewModel
}
  1. Getting the publisher response on the main thread (not necessary).
  2. Calling sink(receiveCompletion:receiveValue:) on the publisher with completion and receive value block will give you a subscriber. The first closure executes when it receives Subscribers.Completion, which is an enumeration that indicates whether the publisher finished normally or failed with an error. The second closure executes when it receives an element from the publisher which in this case will be our view model.
let publisher = networkService.publisher()
.receive(on: DispatchQueue.main)
.replaceError(with: DefaultModel())
.eraseToAnyPublisher() //1
let subscriber = publisher.assign(to: \.viewModel, on: self) //2
  1. As you can see this approach is similar to the previous example with the exception of error handling. You can see that i am replacing all the errors with a DefaultModel object and transforming the publisher to type AnyPublisher<ModelObject, Never>. Reason being the assign(to:on:) function requires the Publisher’s Failure enum to be of type Never.
  2. Finally create a subscriber and assign the published view model object to the viewModel property in self.

--

--

A Software Engineer with a passion for technology. Working as an iOS Developer @BBC

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Melvin John

Melvin John

A Software Engineer with a passion for technology. Working as an iOS Developer @BBC