Network Request With Swift Combine
Hi everyone, its been a little while since i blogged here but better late than never.
Hope everyone started to settle in with the mammoths of new and exciting changes from WWDC 2019. I for one started to experiment with Swift UI and Combine frameworks and must say they are pretty amazing to work with even tho it runs on magic and pixie dust 😅.
In this blog i want to show you how to perform a network request and parse the response using the new Combine framework.
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.
Subscriber: “A protocol that declares a type that can receive input from a publisher.” from the Docs. You can think of Subscriber as an observer, which you can use to subscribe to a publisher to receive values/updates.
Let’s look at how one can use these two protocols to perform a network request and parse the response.
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
- Gives us a DataTaskPublisher object. It’s a publisher to go and get response from end point and publish a value.
- Extract the Data object from response.
- Decode Data to a model object of your choice, using JSONDecoder.
- Map any error to an appropriate error type of your choice.
- 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
}
- Getting the publisher response on the main thread (not necessary).
- 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.
You don’t necessarily need to hold on to the subscribers unless you want to cancel a subscription which could be because the publisher may continue to emit elements until it completes normally or fails.
The Combine framework also provides one other built-in subscriber assign(to:on:), this assigns published elements from a Publisher to a property of a given object, using a key path to indicate the property.
let publisher = networkService.publisher()
.receive(on: DispatchQueue.main)
.replaceError(with: DefaultModel())
.eraseToAnyPublisher() //1let subscriber = publisher.assign(to: \.viewModel, on: self) //2
- 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.
- Finally create a subscriber and assign the published view model object to the viewModel property in self.
This is a small dive into making a network request and parsing the response using the Combine framework. Hope you found it useful. I hope to do more tutorials using the Combine and SwiftUI Framework. And finally if i missed anything or you think there’s a better way, please let me know 🙂.