Combine: Debugging Combine Messages

Combine: Debugging Combine Messages

With these short code snippets you will be able add debug messages to your Combine workflow to understand what is happening behind the scenes.

This brief overview will demonstrate some basic features that may come in handy when working with publishers in Combine, Apple's framework to handle asynchronous events by combining event-processing operators. The Publisher protocol declares a type that transmits a sequence of values over time that subscribers can receive as input by adopting the Subscriber protocol.

When dealing with Combine pipelines it is often not so straightforward what is happening behind the scenes. To get a better understanding, you can add the .print() operator to the publisher.

For example:

import Foundation
import Combine

let publisher = (0...10).publisher

publisher
    .print("DEBUG")
    .sink {
        print ($0)
    }

On the console you can now see every step the publisher is taking:

DEBUG: receive subscription: (0...10)
DEBUG: request unlimited
DEBUG: receive value: (0)
0
DEBUG: receive value: (1)
1
DEBUG: receive value: (2)
2
DEBUG: receive value: (3)
3
DEBUG: receive value: (4)
4
DEBUG: receive value: (5)
5
DEBUG: receive value: (6)
6
DEBUG: receive value: (7)
7
DEBUG: receive value: (8)
8
DEBUG: receive value: (9)
9
DEBUG: receive value: (10)
10
DEBUG: receive finished

Using the example of a URLSession.shared.dataTaskPublisher(for: URL) to load some placeholder JSON data from https://jsonplaceholder.typicode.com/posts, you can add debug messages to every step of the process to trace the publisher behavior:

guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else {
    fatalError("Invalid URL")
}

let request = URLSession.shared.dataTaskPublisher(for: url)

let subscription = request
    .handleEvents(receiveSubscription: { _ in print("Subscription Received") },
                  receiveOutput: { _, _ in print("Received Output") },
                  receiveCompletion: { _ in print("Received Completion") },
                  receiveCancel: { print("Received Cancel") },
                  receiveRequest: {_ in print("Received Request") })
    .sink(receiveCompletion: {  print($0) }, receiveValue: { data, response in
    print("downloaded data: \(data)")
})

On the console, you can follow the url request as it happens:

Subscription Received
Received Request
Received Output
downloaded data: 27520 bytes
Received Completion
finished

Where to go next?

If you are interested into knowing more about working with Combine and the various operators available to shape the sequence of values send by publishers and received by subscribers, check our other articles on Combine that will be released over the next days and weeks.

They will cover how to work with filters, sequence, and transformation operators or timers, how to use the dataTaskPublisher, how to combine publishers as well as how to debug Combine messages, and much more.