Skip to Content
Course content

85: Publishers and Subscribers Explained

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Wait, what actually is a Publisher and a Subscriber in plain English?

Think of a Publisher as a radio station. It doesn't actually care who is listening; it just broadcasts a stream of data (or a single value, or an error) out into the void. In Swift, a Publisher is a protocol that defines how those values are produced and sent.

A Subscriber, on the other hand, is like the person with the radio. They tune into a specific frequency and decide what to do with the information they receive. Until a Subscriber "tunes in," the Publisher usually doesn't even start doing any work. This is a key concept: Combine is "lazy."

Let's look at a PassthroughSubject. I like using this when I'm starting out because it's a Publisher that lets me manually "push" values into the stream.

import Combine

// Our "Radio Station"
let weatherStation = PassthroughSubject<String, Never>()

// Our "Listener"
let weatherSubscriber = weatherStation.sink { update in
    print("The weather report says: \(update)")
}

// Broadcasting values
weatherStation.send("Sunny, 75°F")
weatherStation.send("Rainy, 62°F")

In this snippet, the PassthroughSubject is the Publisher. The .sink call creates an anonymous Subscriber that simply prints whatever comes through. If I hadn't called .sink, calling .send would do absolutely nothing.

Why do I keep seeing AnyCancellable and .store(in:)?

This is where most people get tripped up. When you subscribe to a publisher, the sink method returns a "ticket" called an AnyCancellable. This object represents the active connection between the subscriber and the publisher.

If that ticket is destroyed (goes out of scope), the subscription is cancelled immediately. I've seen plenty of developers wonder why their code "isn't working," only to realize they didn't save the cancellable. The subscription was created and then instantly deleted because it wasn't stored anywhere.

Usually, we keep a set of these tickets in a property on our class so the subscriptions live as long as the class does:

class WeatherMonitor {
    var cancellables = Set<AnyCancellable>()
    
    func startListening(to station: PassthroughSubject<String, Never>) {
        station.sink { update in
            print("Monitoring: \(update)")
        }
        .store(in: &cancellables) // Keep the ticket!
    }
}

By storing it in a set, you're telling Swift: "Keep this connection alive until this WeatherMonitor instance is destroyed."

Is .sink the only way to subscribe?

Not at all, but it's the most common. .sink is a convenience method that creates a subscriber for you on the fly. However, you can actually create your own custom Subscriber by conforming to the Subscriber protocol.

I'll be honest: you will rarely write your own Subscriber from scratch in a professional project. It's a lot of boilerplate involving "demand" (telling the publisher how many items you're willing to receive). Instead, you'll mostly use .sink or .assign(to:on:).

.assign is particularly useful when you want a publisher to directly update a property on an object—like updating a UI label when a network request finishes. It removes the need for a closure entirely.

class WeatherDisplay {
    var currentTemperature: String = ""
}

let display = WeatherDisplay()
let tempPublisher = PassthroughSubject<String, Never>()

tempPublisher
    .assign(to: \.currentTemperature, on: display)
    .store(in: &cancellables)

tempPublisher.send("82°F")
print(display.currentTemperature) // Outputs: 82°F



📋 Practical Task

Build a Thermal Overload Alert System

Your goal is to create a system that monitors a stream of temperature readings and triggers an alert only when the temperature exceeds a certain threshold.

  • Create a PassthroughSubject that emits Int values.
  • Create a Cancellable storage set to keep your subscription alive.
  • Use .sink to subscribe to the temperature stream.
  • Inside the subscriber, print "Temperature Stable" if the value is 100 or below, and "CRITICAL OVERHEAT!" if the value is above 100.
  • Send at least three different values through the subject (e.g., 70, 110, 90) to verify that your logic handles both cases correctly.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.