# How Reactive Binding Works in SectionKit: A Complete Guide to @SKPublished and Combine

> Discover how SectionKit's @SKPublished and @SKBinding leverage Combine for seamless reactive binding. Enable automatic UI updates without manual reloads in your Swift projects.

- Repository: [神奇/sectionkit](https://github.com/linhay/sectionkit)
- Tags: deep-dive
- Published: 2026-03-06

---

**SectionKit implements reactive binding through pure-Swift property wrappers `@SKPublished` and `@SKBinding` built on Combine, enabling automatic UI updates when state changes without manual reloads.**

SectionKit is an open-source Swift framework that simplifies collection view management through a reactive data flow architecture. Understanding how reactive binding works in SectionKit allows developers to build declarative, fine-grained UI updates that sync automatically with underlying state changes using standard Combine publishers.

## Core Components of the Reactive System

SectionKit's reactive architecture centers on four interconnected components that work together to provide type-safe, memory-efficient data binding.

### @SKPublished Property Wrapper

The `@SKPublished` property wrapper, defined in [`Sources/SectionKit/Common/SKPublished.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/Common/SKPublished.swift), serves as the primary state holder for reactive values. When you annotate a property with `@SKPublished`, the wrapper creates an underlying `SKPublishedValue` instance that acts as a Combine publisher.

`SKPublishedValue` can wrap either a `CurrentValueSubject` (the default) or a `PassthroughSubject` depending on the `SKPublishedKind` configuration. The publisher executes attached `SKPublishedTransform` callbacks—such as `removeDuplicates` or `receiveOnMainQueue`—before emitting values to subscribers.

```swift
@SKPublished var counter = 0
@SKPublished var isSelected = false

```

### @SKBinding for Two-Way Connections

Defined in [`Sources/SectionKit/Common/SKBinding.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/Common/SKBinding.swift), the `@SKBinding` property wrapper exposes a getter and setter pair alongside a `changedPublisher`. Unlike `@SKPublished`, which owns the state, `SKBinding` can reference arbitrary storage via key-paths, `CurrentValueSubject` instances, or other `SKBinding` objects, enabling composition of reactive chains.

```swift
let binding = SKBinding(on: model, keyPath: \.isSelected)
binding.wrappedValue = true  // Equivalent to model.isSelected = true

```

### The bind(_:) Extension Method

`SKPublishedValue` provides a `bind(_:)` convenience method that immediately delivers the current value on the main thread before subscribing to future updates. This method returns an `AnyCancellable` for memory management, ensuring subscribers can store the token in a `Set<AnyCancellable>` or similar collection.

```swift
viewModel.$counter.bind { [weak self] newValue in
    self?.counterLabel.text = "Count: \(newValue)"
}.store(in: &cancellables)

```

### SKCollectionViewController Integration

The UI layer `SKCollectionViewController` (located in [`Sources/SectionUI/CollectionView/SectionCollectionView/SKCollectionViewController.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionUI/CollectionView/SectionCollectionView/SKCollectionViewController.swift)) hosts a `manager` that coordinates sections and cells. Cells configured with `SKConfigurableView` receive models containing `@SKPublished` properties, establishing per-cell reactive subscriptions during the `config(_:)` lifecycle.

## The Reactive Data Flow in Practice

SectionKit implements a five-step data flow that connects state changes to UI updates without requiring manual diff calculations or view reloads.

**1. Declare reactive state with `@SKPublished`**

Properties annotated with `@SKPublished` automatically generate a projected value (accessed via `$`) that returns the underlying `SKPublishedValue` publisher.

```swift
class CounterVM {
    @SKPublished var count = 0
}

```

**2. Subscribe to changes using `bind`**

Subscribers receive the current value immediately on the main thread, followed by all subsequent changes. This pattern eliminates the need for initial manual UI configuration.

```swift
vm.$count.bind { [weak self] v in
    self?.label.text = "Count: \(v)"
}.store(in: &bag)

```

**3. Update values through the wrapper**

Assignments to the wrapped value trigger the publisher, automatically notifying all bound subscribers.

```swift
vm.count += 1  // Triggers UI update

```

**4. Leverage `SKBinding` for complex getters and setters**

When binding UI controls directly to model properties, `SKBinding` provides a lightweight abstraction over key-paths or subjects.

```swift
let binding = SKBinding(on: model, keyPath: \.title)
binding.changedPublisher
    .sink { print("Title changed to \($0)") }
    .store(in: &bag)

```

**5. Establish cell-level subscriptions during configuration**

Each cell maintains its own `Set<AnyCancellable>`, clearing previous subscriptions in `config(_:)` to prevent memory leaks while ensuring reactive updates persist across reuse cycles.

## Implementing Reactive Binding in Collection Views

The [`ReactiveDataViewController.swift`](https://github.com/linhay/sectionkit/blob/main/ReactiveDataViewController.swift) example in the repository demonstrates end-to-end reactive patterns at both the view-controller and cell levels.

### View-Controller Level Binding

```swift
class CounterVC: SKCollectionViewController {
    let vm = CounterVM()
    private var bag = Set<AnyCancellable>()
    private let label = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        vm.$count.bind { [weak self] v in
            self?.label.text = "Count: \(v)"
        }.store(in: &bag)
    }

    @objc func increment() { 
        vm.count += 1 
    }
}

```

### Cell-Level Reactive Configuration

Cells implementing `SKConfigurableView` establish bindings during configuration, enabling fine-grained animations and state changes without reloading the entire collection view.

```swift
class ColorCellModel {
    @SKPublished var isSelected = false
    var color: UIColor?
}

class ColorCell: UICollectionViewCell, SKConfigurableView {
    private var bag = Set<AnyCancellable>()

    func config(_ model: ColorCellModel) {
        bag.removeAll()
        contentView.backgroundColor = model.color ?? .gray
        
        model.$isSelected.bind { [weak self] selected in
            UIView.animate(withDuration: 0.3) {
                self?.layer.borderWidth = selected ? 4 : 0
                self?.transform = selected 
                    ? CGAffineTransform(scaleX: 0.9, y: 0.9) 
                    : .identity
            }
        }.store(in: &bag)
    }
}

```

## Advanced Usage with SKBinding

`SKBinding` supports composition through initialization with `CurrentValueSubject` or other `SKBinding` instances, enabling derived state and complex reactive chains.

```swift
// Binding to a subject
let subject = CurrentValueSubject<Bool, Never>(false)
let binding = SKBinding(subject: subject)

// Binding to another binding
let derivedBinding = SKBinding(binding: originalBinding)

```

## Summary

- **@SKPublished** creates `SKPublishedValue` publishers that emit changes through Combine `CurrentValueSubject` or `PassthroughSubject` instances, located in [`Sources/SectionKit/Common/SKPublished.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/Common/SKPublished.swift).
- **SKPublishedValue** supports transform chains like `removeDuplicates` and guarantees main-thread delivery through the `bind(_:)` method.
- **@SKBinding** provides a property wrapper interface for arbitrary getter/setter pairs, enabling two-way data flow without owning the underlying storage.
- **Cell-level integration** occurs through `SKConfigurableView`, where cells store `AnyCancellable` tokens in per-instance bags to manage subscription lifecycles.
- **Thread safety** is handled internally by `bind(_:)`, which ensures initial values and updates arrive on the main queue suitable for UIKit operations.

## Frequently Asked Questions

### What is the difference between @SKPublished and @SKBinding in SectionKit?

`@SKPublished` owns the state and acts as the source of truth, creating a publisher that emits when the wrapped value changes. `@SKBinding` does not own state; it provides read-write access to existing storage via key-paths or subjects and exposes a `changedPublisher` for observation. Use `@SKPublished` for view models and models, and `@SKBinding` when connecting UI controls to existing properties.

### How does SectionKit ensure thread safety when updating UI?

The `bind(_:)` extension on `SKPublishedValue` automatically delivers the current value and all subsequent updates on the main thread. This main-queue guarantee ensures that UI updates triggered by reactive bindings occur safely within UIKit's requirements, regardless of which thread initiated the state change.

### Can I use SectionKit's reactive binding without SKCollectionViewController?

Yes. The reactive components `@SKPublished`, `SKPublishedValue`, and `SKBinding` are defined in the core `SectionKit` module and have no dependency on `SKCollectionViewController` or UIKit. You can use them in any Swift context requiring Combine-based reactivity, including SwiftUI integration or standalone view controllers.

### What Combine publishers does SKPublishedValue use internally?

According to the source code in [`SKPublished.swift`](https://github.com/linhay/sectionkit/blob/main/SKPublished.swift), `SKPublishedValue` uses `CurrentValueSubject` by default (maintaining the current value for new subscribers) or `PassthroughSubject` when configured with `SKPublishedKind.passthrough`. Both publishers conform to Combine's `Subject` protocol and support standard operators like `sink` and `assign`.