← thecodex.expert · The Codex Family of Knowledge
Swift

The Combine Framework

For a single async result, Swift’s own async/await is usually simpler now — Combine’s real, ongoing strength is genuine streams of values that keep arriving over time, like user input or property changes.

Swift 6 Best for streams, not one-shot results Last verified:
Canonical Definition

A Publisher emits a sequence of values (and possibly a completion or failure) over time; a Subscriber receives them, most commonly via sink (a simple closure-based subscription) or assign (directly writing emitted values to a property). @Published, applied to a property, automatically creates a Publisher that emits every time the property changes — the mechanism SwiftUI's data-binding relies on. Since async/await matured into Swift's standard tool for asynchronous work, Apple's own guidance and much of the community have shifted toward preferring it for single-result async operations, reserving Combine specifically for genuine streams — ongoing sequences of values like user text input or repeated property changes — where Combine's operators (map, filter, debounce, and others) still offer real value async/await alone doesn't directly provide.

@Published: turning a property into a stream

Subscribing to $name's sink fires immediately with name's CURRENT value at the moment of subscription (the projected publisher behaves like a CurrentValueSubject) — then again on every subsequent assignment. Apple's own documentation example shows this exactly: subscribing when temperature is already 20 prints "Temperature now: 20.0" immediately, before the later assignment to 25 prints again.

Swiftpublished_basics.swift
import Combine

class UserViewModel: ObservableObject {
    @Published var name: String = ""
}

let viewModel = UserViewModel()
var cancellables = Set<AnyCancellable>()

viewModel.$name.sink { newValue in
    print("Name changed to: \(newValue)")
}.store(in: &cancellables)
// fires immediately on subscription with the CURRENT value: prints "Name changed to: " (name is still "")

viewModel.name = "Alice"   // prints "Name changed to: Alice"
viewModel.name = "Bob"       // prints "Name changed to: Bob"

Operators: transforming a stream

map and filter behave conceptually just like their Array counterparts, but operate on VALUES ARRIVING OVER TIME rather than a fixed, already-complete collection.

Swiftcombine_operators.swift
let numbers = PassthroughSubject<Int, Never>()

numbers
    .filter { $0 % 2 == 0 }
    .map { $0 * 10 }
    .sink { print($0) }
    .store(in: &cancellables)

numbers.send(3)   // filtered out — odd
numbers.send(4)   // 40 — even, passes the filter, then multiplied by 10 by map (not "doubled" — the closure is { $0 * 10 })

When to reach for Combine vs. async/await

A single network call fetching one result is now usually simpler as an async function; Combine's real, ongoing strength is genuine ongoing streams — debouncing rapid text-field input, or reacting continuously to a property that changes many times over an app's lifetime.

Sources

1
Apple. Combine, developer.apple.com/documentation/combine.