How to Integrate SectionKit with Combine: Reactive Collection Views in Swift
SectionKit provides native Combine integration through the subscribe(models:) method on SKCSingleTypeSection and the @SKPublished property wrapper, automatically handling data binding, cancellable lifecycle, and main-thread UI updates.
SectionKit is a Swift framework designed for building modular, section-based UICollectionView architectures with minimal boilerplate. When you integrate SectionKit with Combine, you establish a unidirectional data flow where publishers drive UI updates without explicit reloadData() calls, as coordinated by SKCManager.
Subscribing Sections to Combine Publishers
The primary integration point lives in Sources/SectionKit/CollectionSingleTypeSection/Entities/SKCSingleTypeSection+subscribe.swift. The subscribe(models:) method accepts any Combine publisher emitting an array of models—or a single model—and binds it to the section's internal state.
Automatic Thread Safety and Cancellable Management
This method stores the resulting AnyCancellable internally within the section’s publishers container, eliminating the need for you to maintain separate references. It automatically forces the stream onto the main run loop using receive(on: RunLoop.main) before applying data changes.
import Combine
import SectionKit
class ViewController: SKCollectionViewController {
private var modelsSubject = CurrentValueSubject<[MyCell.Model], Never>([])
private lazy var section = MyCell.wrapperToSingleTypeSection()
override func viewDidLoad() {
super.viewDidLoad()
// Subscribe and chain fluently
section.subscribe(models: modelsSubject)
manager.reload(section)
}
}
Reactive State Management with @SKPublished
For fine-grained reactive state outside of array-based sections, the framework provides @SKPublished, defined in Sources/SectionKit/Common/SKPublished.swift. This property wrapper encapsulates values in a CurrentValueSubject or PassthroughSubject, emitting Combine events whenever the wrapped value changes.
import Combine
import SectionKit
final class CounterViewModel {
@SKPublished var count: Int = 0
var countPublisher: AnyPublisher<Int, Never> {
$count.publisher
}
func increment() {
count += 1 // Automatically emits to subscribers
}
}
// Inside a cell
func config(_ model: Model, viewModel: CounterViewModel) {
cancellable = viewModel.$count.bind { [weak self] newCount in
self?.button.setTitle("\(newCount)", for: .normal)
}
}
The SKPublishedTransform type provides static helpers such as removeDuplicates() and receiveOnMainQueue() for composing publisher chains before they reach the section.
Manager Coordination and Update Propagation
SKCManager, implemented in Sources/SectionKit/CollectionBase/SKCManager.swift, acts as the central mediator between sections and the collection view. When a publisher emits new data, the section calls apply(models) internally; the manager receives this change through the injected SKCSectionInjection and triggers the appropriate animations (insertions, deletions, or reloads).
As described in Sources/SectionKit/AGENTS.md, this architecture ensures that all UI updates occur on the main thread while allowing you to emit values from background queues.
Complete Working Example
The repository includes a concrete demonstration in Example/Data/SubscribeDataWithCombineViewController.swift, showing a CurrentValueSubject<[TextCell.Model], Never> feeding a single-type section. Adding items to the subject instantly updates the collection view without manual intervention.
import UIKit
import Combine
import SectionKit
class SubscribeDataWithCombineViewController: SKCollectionViewController {
private let subject = CurrentValueSubject<[TextCell.Model], Never>([])
private lazy var textSection = TextCell.wrapperToSingleTypeSection()
override func viewDidLoad() {
super.viewDidLoad()
// Bind publisher to section—cancellable stored automatically
textSection.subscribe(models: subject)
manager.reload(textSection)
// Simulate async data loading
DispatchQueue.global().async { [weak self] in
let newData = (0..<5).map { TextCell.Model(text: "Row \($0)") }
self?.subject.send(newData)
}
}
}
This pattern works with any upstream publisher, including those transformed with filter, map, or removeDuplicates operators.
Summary
subscribe(models:): Binds any Combine publisher toSKCSingleTypeSection, storing cancellables internally and enforcing main-thread delivery viaSources/SectionKit/CollectionSingleTypeSection/Entities/SKCSingleTypeSection+subscribe.swift.@SKPublished: Provides a general-purpose property wrapper inSources/SectionKit/Common/SKPublished.swiftfor reactive state that bridges SwiftUI-style observation into UIKit collection views.SKCManager: Mediates between section updates and collection view animations according toSources/SectionKit/AGENTS.md, requiring no manualperformBatchUpdatescalls when publishers emit.- Thread Safety: All data emissions are automatically scheduled on the main run loop inside the subscription, preventing UI consistency errors regardless of the upstream queue.
Frequently Asked Questions
Does SectionKit require manual sink management for Combine subscriptions?
No. When you call subscribe(models:) on SKCSingleTypeSection, the framework stores the resulting AnyCancellable in the section's internal publishers set. The subscription lifecycle matches the section's lifecycle, eliminating retain cycles and the need to maintain separate Set<AnyCancellable> properties in your view controller.
How does SectionKit handle threading when using Combine publishers?
The implementation in SKCSingleTypeSection+subscribe.swift applies receive(on: RunLoop.main) to the publisher chain before updating the model array. This ensures that SKCManager always receives data changes on the main thread, preventing the crashes and inconsistencies that occur when updating UICollectionView from background queues.
Can @SKPublished be used outside of collection view cells?
Yes. @SKPublished is a general-purpose property wrapper defined in Sources/SectionKit/Common/SKPublished.swift that functions in any view model, controller, or service layer. It wraps values in CurrentValueSubject or PassthroughSubject and supports composition with SKPublishedTransform helpers for debouncing, deduplication, or queue-shifting.
What publisher types are compatible with subscribe(models:)?
Any Combine publisher that outputs an array of models conforming to the section's expected type—or a single model when using single-item variants—and never fails (where Failure == Never) works with subscribe(models:). Common implementations include CurrentValueSubject, PassthroughSubject, @Published projections, and transformed publishers using map or compactMap operators.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →