How it works
SubscriptionView is a view that automatically connects to a Combine Publisher and forwards every value it emits to an action closure. It exists to bridge an external, asynchronous stream of events into your view hierarchy without writing your own Cancellable bookkeeping: the subscription is created when the view appears and torn down when it disappears, so its lifetime is tied to the view that owns it. Reach for it when a piece of UI needs to react to a publisher — a timer, a notification, a model's @Published change — and you want SwiftUI to manage the subscription for you. In practice you rarely instantiate it directly; the onReceive(_:perform:) modifier wraps SubscriptionView so the same machinery hangs off any existing view.
Provide the content with @ViewBuilder content
The first thing
SubscriptionViewwraps is the view you actually want on screen, supplied through its@ViewBuilder contentparameter and exposed as thecontentproperty. The subscription rides alongside this content rather than replacing it — in the example, the membership card built from thecrown.fillImage, thePro MembershipText, and the plan rows would be thecontentthat aSubscriptionView(or theonReceiveit backs) attaches to.Attach the stream through the publisher parameter
The
publisherparameter takes any CombinePublisherwhoseFailureisNever, and the view subscribes to it on your behalf. This is where you hand over the event source — for the demo screen that could be a publisher tracking entitlement or price changes for theMonthlyandYearlyplans, so the UI updates the moment the stream emits.Respond to each value with the action closure
Every element the publisher delivers is passed to the
actionclosure, which runs on the main run loop so it is safe to mutate view state from it. Here that closure is the natural home for assigning the result into@State private var selected, exactly as theonTapGesture { selected = plan }handler does — except driven by the publisher instead of a tap.Let the view manage the subscription lifetime
Because
SubscriptionViewconforms toView, it participates in the normal appearance lifecycle: it subscribes when inserted into the hierarchy and cancels when removed, so there is noCancellableto store. Theselected == plancheckmark logic and theSubscribeButtonkeep working unchanged while the subscription quietly keeps state in sync underneath them.Prefer onReceive(_:perform:) in everyday code
Apple exposes this capability primarily through the
onReceive(_:perform:)view modifier, which constructs aSubscriptionViewfor you and layers it onto an existing view. Rather than rebuilding theVStackaround an explicitSubscriptionView, you would call.onReceive(planPublisher) { selected = $0 }on the body and get the identical behavior with less ceremony.
.onReceive to the VStack body wired to a Timer.publish(every:1, on:.main, in:.common).autoconnect() publisher whose closure toggles selected between "Monthly" and "Yearly", and watch the checkmark move on its own as the subscription emits.Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct SubscriptionViewDemo: View {
@State private var selected = "Yearly"
var body: some View {
VStack(spacing: 16) {
Image(systemName: "crown.fill")
.font(.largeTitle)
.foregroundStyle(.yellow)
Text("Pro Membership")
.font(.title2.bold())
Text("Unlock all features")
.foregroundStyle(.secondary)
ForEach(["Monthly", "Yearly"], id: \.self) { plan in
HStack {
Text(plan)
Spacer()
Image(systemName: selected == plan ? "checkmark.circle.fill" : "circle")
}
.padding()
.background(selected == plan ? Color.blue.opacity(0.15) : Color.gray.opacity(0.1))
.clipShape(RoundedRectangle(cornerRadius: 10))
.onTapGesture { selected = plan }
}
Button("Subscribe") {}
.buttonStyle(.borderedProminent)
}
.padding()
}
}