How SKCManager Handles UICollectionView Delegate and DataSource Calls in SectionKit
SKCManager does not implement UICollectionView delegate or dataSource methods directly; instead, it wires specialized forward objects that use a chain-of-responsibility pattern to dispatch calls to registered handlers.
SKCManager serves as the central orchestrator in SectionKit, a Swift library that simplifies UICollectionView management through a section-based architecture. When initializing an SKCManager instance with a UICollectionView, how SKCManager handles UICollectionView delegate and dataSource calls becomes clear through its unique forwarding mechanism rather than direct protocol implementation.
The Wiring Architecture in SKCManager
Inside Sources/SectionKit/CollectionBase/SKCManager.swift (lines 30‑38), the setup(sectionView:) method replaces the collection view’s delegate and dataSource with specialized forward objects:
private func setup(sectionView: UICollectionView) {
self.sectionView = sectionView
// The collection view’s delegate / data source are replaced
sectionView.delegate = flowLayoutForward // SKCDelegateForward (inherits UICollectionViewDelegate)
sectionView.dataSource = dataSourceForward // SKCDataSourceForward (inherits UICollectionViewDataSource)
sectionView.prefetchDataSource = prefetchForward // SKCDataSourcePrefetchingForward
// Register the manager’s own concrete forward objects
flowLayoutForward.add(delegate) // SKCDelegate (handles generic delegate callbacks)
flowLayoutForward.add(flowlayoutDelegate)// SKCDelegateFlowLayout (handles flow‑layout callbacks)
dataSourceForward.add(dataSource) // SKCDataSource (handles data‑source callbacks)
prefetchForward.add(prefetching) // SKCDataSourcePrefetching
}
This architecture ensures that every UICollectionView callback is intercepted by a forward object rather than the manager itself.
Chain-of-Responsibility Pattern
The forward objects—SKCDataSourceForward, SKCDelegateForward, and SKCDelegateFlowLayoutForward—implement a chain-of-responsibility pattern to determine which handler should process a given call.
Forward Items vs Observer Items
Each forward object maintains two distinct arrays:
forwardItems(oruiForwards): Contains objects conforming toSKCDataSourceForwardableProtocolorSKCDelegateForwardProtocol. These are potential handlers—the first item that returns a concrete value (.handle) stops the chain.observerItems(oruiObservers): Contains objects conforming toSKCDataSourceObserverProtocolorSKCDelegateObserverProtocol. These are observers that receive notification of the final value after the chain resolves.
The Find Method Implementation
The core dispatch logic resides in Sources/SectionKit/CollectionBaseProtocol/SKCDataSource/SKCDataSourceForward.swift (lines 99‑108). The find<T> method iterates through forwardItems in reverse order, returning the first concrete result:
func find<T>(`default`: @autoclosure () -> T,
_ task: (_ item: SKCDataSourceForwardableProtocol) -> SKHandleResult<T>) -> T {
for item in forwardItems.reversed() { // iterate back‑to‑front
let result = task(item)
switch result {
case .handle(let value): return value // first concrete result wins
case .next: break
}
}
return `default`() // fallback if none handle
}
SKCDelegateForward.swift uses an identical pattern for delegate methods such as shouldSelectItemAt and willDisplay.
Delegate and DataSource Method Routing
Consider the flow when collectionView(_:cellForItemAt:) is invoked:
- The collection view calls its dataSource’s
cellForItemAt. - Because
sectionView.dataSourcepoints todataSourceForward, the call lands inSKCDataSourceForward. SKCDataSourceForwardinvokesfind, iterating throughforwardItems(the manager’sSKCDataSourceis typically first). IfSKCDataSourcereturns.handle(cell), that cell is used; otherwise a default debug cell is returned.- After the cell is resolved,
observenotifies everyobserverItem(e.g., a custom logger) with the final cell value.
All other UICollectionView callbacks follow this identical pattern—they are routed through the forward objects, allowing developers to inject custom behaviors without subclassing UICollectionView.
Practical Implementation Examples
Basic Manager Initialisation
let layout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
let manager = SKCManager(sectionView: collectionView) // delegates & data sources wired automatically
Injecting a Custom Section
class MySection: SKCSingleTypeSection<String> {
override func config(sectionView: UICollectionView) {
// register cell class / nib
collectionView.register(MyCell.self, forCellWithReuseIdentifier: "MyCell")
}
override func cell(collectionView: UICollectionView,
indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "MyCell", for: indexPath) as! MyCell
cell.label.text = models[indexPath.item]
return cell
}
}
// Add the section to the manager
manager.append(MySection(models: ["A", "B", "C"]))
Adding a Custom Delegate Handler
struct SizeProvider: SKCDelegateFlowLayoutForwardProtocol {
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> SKHandleResult<CGSize> {
return .handle(CGSize(width: 100, height: 50))
}
}
// Attach to the flow‑layout forward
manager.flowLayoutForward.add(SizeProvider())
Key Source Files
These files together explain how SKCManager cleanly abstracts away the raw UICollectionView delegate/data‑source plumbing, allowing developers to focus on section logic while still being able to inject custom behaviours at any point in the call chain.
Summary
- SKCManager acts as the central coordinator but never implements
UICollectionViewDelegateorUICollectionViewDataSourcedirectly. - Forward objects (
SKCDataSourceForward,SKCDelegateForward,SKCDelegateFlowLayoutForward) are wired as the collection view’s official delegate and dataSource. - Chain-of-responsibility pattern allows multiple handlers to attempt processing a call; the first handler returning
.handle(value)provides the result, while observers receive the final value. - Reversed iteration through
forwardItemsensures that the most recently added handlers take precedence. - Zero subclassing required—developers inject custom behavior by conforming to forward protocols and adding instances to the manager’s forward objects.
Frequently Asked Questions
How does SKCManager differ from traditional UICollectionViewController implementations?
Unlike UICollectionViewController, which typically implements delegate and dataSource protocols directly, SKCManager delegates all callbacks to specialized forward objects. This decouples the manager from specific UI logic, allowing multiple independent handlers to process collection view events through a chain-of-responsibility pattern.
What is the difference between forwardItems and observerItems in SKCDataSourceForward?
forwardItems contains objects that can handle delegate or dataSource calls by returning concrete values via SKHandleResult.handle(value). The chain iterates through these until one returns a handled result. observerItems contains objects that observe the final outcome after the chain resolves, receiving the value for side effects like logging or analytics but cannot alter the return value.
Can I use multiple custom handlers for the same delegate method?
Yes. You can add multiple objects conforming to SKCDelegateForwardProtocol or SKCDataSourceForwardableProtocol to the manager’s forward objects. When a delegate method is called, the chain processes them in reverse order of addition, and the first handler returning .handle(value) provides the result. Subsequent handlers in the chain are skipped for that call, but observers still receive the final value.
Where does the fallback logic execute if no handler processes a call?
If no item in the forwardItems array returns .handle(value), the find method executes its @autoclosure default parameter. For example, in SKCDataSourceForward, if no handler provides a cell for cellForItemAt, the default closure returns a debug cell or zero count, ensuring the collection view always receives a valid response.
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 →