Swift
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Optionals
-
Section 4: Object-Oriented and Value Types
-
Section 5: Memory Management
-
Section 6: Generics and Error Handling
-
Section 7: Concurrency
-
Section 8: Working with Collections
-
Section 9: Codable and Data Handling
-
Section 10: Protocol-Oriented Programming
-
Section 11: Testing and Tooling
-
Section 12: Practical Projects
-
Section 13: Interview Practice
-
Section 14: More Practice Exercises
-
Section 15: More Standard Library
-
Section 16: Advanced Concurrency
-
Section 17: Foundation Framework Deep Dive
-
Section 18: URLSession and Networking Deep Dive
-
Section 19: Combine Framework
-
Section 20: SwiftUI Fundamentals for Swift Developers
-
Section 21: Server-Side Swift with Vapor
-
Section 22: Swift Package Manager Deep Dive
-
Section 23: Swift Concurrency Deep Dive
-
Section 24: More Language Features
-
Section 25: Error Handling Deep Dive
-
Section 26: Testing Deep Dive
-
Section 27: Data Structures and Algorithms in Swift
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Swift Macros (Swift 5.9+)
-
Section 31: Property Wrappers Ecosystem
-
Section 32: Swift Interop Deep Dive
-
Section 33: iOS App Architecture Patterns
-
Section 34: Performance and Debugging
-
Section 35: App Distribution and CI/CD
-
Section 36: More Practical Projects
-
Section 37: SwiftData and Persistence
-
Section 38: More Design Patterns
-
Section 39: More Review and Practice
-
Section 40: More Foundation Deep Dive
-
Section 41: Advanced Collections in Swift
-
Section 42: Advanced Generics Practice
-
Section 43: UIKit for Legacy and Hybrid Apps
-
Section 44: watchOS and visionOS Development Basics
-
Section 45: More Networking Patterns
-
Section 46: More Testing Practice
-
Section 47: Accessibility in Swift Apps
-
Section 48: Localization
-
Section 49: More Practical Projects Round 2
-
Section 50: Swift Charts Framework
-
Section 51: More Interview and Algorithm Practice
-
Section 52: Final Practice and Mastery
-
Section 53: Swift Compiler and Build System
-
Section 54: More Concurrency Practice
-
Section 55: App Store Guidelines and Review
-
Section 56: More Design and Architecture
85: Publishers and Subscribers Explained
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
PassthroughSubjectthat emitsIntvalues. - Create a
Cancellablestorage set to keep your subscription alive. - Use
.sinkto 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.
There are no comments for now.