Notes: 01. Networking with Combine
Prerequisites: Intermediate Swift knowledge, basic Combine knowledge, knowledge of URLSession
.
URLSession supports a variety of operations such as: data transfer tasks to retrieve contents of a URL; download tasks to retrieve the contents of a URL and save it to a file; upload tasks to upload files and data to a URL; stream tasks to stream data between two parties; websocket tasks to connect to websockets.
Only the first operation - data transfer tasks - expose a Combine publisher, taking either a URL or a URLRequest. Let’s look at that in an example.
URL
by passing in a URL for a JSON file, and if it fails use a guard statement to return
example(of: "dataTaskPublisher") {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1") else {
return
}
Create the Combine pipeline, making sure to keep the resulting subscription; otherwise it will immediately be cancelled and the request will never execute.
URLSession.shared
instance, call dataTaskPublisher(for:)
, passing in the url you made earlier.
// 1
URLSession.shared
// 2
.dataTaskPublisher(for: url)
sink(receiveCompletion: receiveValue:)
version, so both errors and received values can be sent to the console. This is very important because networks are prone to failure.
.sink(
receiveCompletion: { completion in
// 3
if case .failure(let err) = completion {
print("Retrieving data failed with error \(err)")
}
}, receiveValue: { data, response in
// 4
print("Retrieved data of size \(data.count), response = \(response)")
})
.store(in: &subscriptions)
sink
is a tuple, containing the Data
object and the URLResponse
from the server. This is very similar to the closure used in normal URLSession
use, but with a simpler publisher abstraction from Combine.
Codable is a very powerful protocol in Swift that provides an encoding and decoding mechanism you should definitely be familiar with. If you’re not, be sure to check out the various courses on raywenderlich.com including, but not limited to, “Encoding and Decoding with Swift” by Cosmin Pupaza.
decode()
operation from Combine.
dataTaskPublisher(for:)
operator.
example(of: "decode") {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1") else {
return
}
URLSession.shared
.dataTaskPublisher(for: url)
dataTaskPublisher
emits a tuple, so you have to use map to grab only the data part of the tuple. Since JSONDecoder.decode can throw an error, you have to put the try
keyword in front of it, which means that you must use tryMap
and not just map
.tryMap { data, _ in
try JSONDecoder().decode(Todo.self, from: data)
}
sink
as before.
.sink(receiveCompletion: { completion in
if case .failure(let err) = completion {
print("Retrieving data failed with error \(err)")
}
}, receiveValue: { object in
print("Retrieved object \(object)")
})
.store(in: &subscriptions)
tryMap
block. The decode(type: decoder:)
operator takes in a decoder instead of a Data
object, instead expecting that data from upstream. You still need to extract out the data part of the tuple, so you still need to use map
. Replace the tryMap
block with the map
operator followed by the decode
operator.
.map(\.data)
.decode(type: Todo.self, decoder: JSONDecoder())