# How to Debug SectionKit Collections: A Step-by-Step Guide

> Debug SectionKit collection issues with this step-by-step guide. Verify frames, section injections, layout pipelines, and use requestPublishers to track lifecycle events.

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

---

**To debug SectionKit collection views, verify the view has a non-zero frame, ensure sections are bound with valid `sectionInjection` instances, inspect the layout plugin pipeline, and leverage the `requestPublishers` system to observe lifecycle events.**

SectionKit is a declarative, data-driven framework built on top of UIKit that simplifies building complex collection views. When cells go missing, layouts break, or updates trigger crashes, understanding the internal mechanism of `SKCManager` and its binding lifecycle is essential to resolve issues efficiently.

## Understanding the SectionKit Architecture

Before debugging, recognize the three critical layers in the linhay/sectionkit repository:

- **`SKCollectionView`** ([`Sources/SectionUI/CollectionView/SectionCollectionView/SKCollectionView.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionUI/CollectionView/SectionCollectionView/SKCollectionView.swift)): The `UICollectionView` subclass that wires the manager and plugin-based layout system.
- **`SKCManager`** ([`Sources/SectionKit/CollectionBase/SKCManager.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/CollectionBase/SKCManager.swift)): The central orchestrator handling section binding, data-source forwarding, and batch-update coordination.
- **`SKCLayoutPlugins`** ([`Sources/SectionUI/CollectionView/SectionCollectionView/FlowLayout/SKCLayoutPlugins.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionUI/CollectionView/SectionCollectionView/FlowLayout/SKCLayoutPlugins.swift)): The modular system that calculates section-specific layout modes.

Most debugging issues stem from disconnects between these layers—particularly when the collection view isn't ready or sections aren't properly bound to the manager.

## Verify the Collection View Has a Non-Zero Frame

`SKCManager` defers all layout-related processing until the collection view has a valid size. In [`SKCManager.swift`](https://github.com/linhay/sectionkit/blob/main/SKCManager.swift), the `setup(request:)` method explicitly checks `sectionView.frame.width > 0 && sectionView.frame.height > 0` before executing layout-related requests.

If you create `SKCollectionView` programmatically, ensure constraints are active before calling manager methods:

```swift
view.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    collectionView.topAnchor.constraint(equalTo: view.topAnchor),
    collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
    collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
    collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
// Wait for layout cycle before appending sections

```

## Check Section Binding and Injection

Sections must be **bound** to a manager via `bind(sections:start:)` in [`SKCManager.swift`](https://github.com/linhay/sectionkit/blob/main/SKCManager.swift). This binding injects a `SKCSectionInjection` object containing the section index and view reference into each section.

If a section's `sectionInjection` property remains `nil`, UI updates are silently ignored. In DEBUG builds, the `security(check:)` method asserts that `sectionInjection != nil`, catching unbound sections early.

To verify binding:

1. Set a breakpoint after `manager.append(section)` or `manager.bind(sections:)`
2. Inspect `section.sectionInjection` — it should be non-nil
3. Check the console for assertion failures when running with `-Onone` optimization

## Inspect the Layout Plugin Collection

Missing or misconfigured plugins often cause incorrect item sizes or absent headers/footers. `SKCManager` collects plugins per-section via `collectSectionLayoutPlugins()` and merges them with global plugins set via `set(pluginModes:)`.

If layout appears wrong:

- Verify `pluginsModes` contains the expected `SKCLayoutPlugins.Mode` objects
- Check that custom sections implement the layout protocol methods returning correct plugin configurations
- Ensure global plugins registered via `set(pluginModes:)` aren't overriding section-specific settings unintentionally

## Monitor Layout Cycles with Request Publishers

`SKCollectionView` exposes Combine publishers that emit during critical lifecycle events. Subscribe to `requestPublishers.layoutSubviews` to confirm when the view completes a layout pass and has valid dimensions.

```swift
import Combine

var cancellables = Set<AnyCancellable>()

collectionView.requestPublishers.layoutSubviews
    .sink { _ in
        print("Layout complete - frame: \(self.collectionView.frame)")
        // Safe to perform scroll-to or section updates here
    }
    .store(in: &cancellables)

```

This publisher fires after `layoutSubviews()` completes, indicating the manager will now process queued requests.

## Analyze Batch Update Behavior

`SKCManager` decides between incremental updates and full `reloadData` based on its static `Configuration`. The method `pick(_:completion:)` wraps `performBatchUpdates`, while `insert(_:at:)`, `remove(_:)`, and `reload(_:)` route through the manager's update logic.

To isolate update bugs:

- Temporarily set `SKCManager.configuration.replaceInsertWithReloadData = false` to force incremental batch updates
- Breakpoint inside `pick(_:completion:)` to verify the completion handler fires
- Check if specific sections trigger crashes when updated—often indicating a mismatch between data source counts and the manager's internal state

## Enable Debug Assertions

SectionKit includes runtime checks that only fire in DEBUG builds. The `security(check:)` method in [`SKCManager.swift`](https://github.com/linhay/sectionkit/blob/main/SKCManager.swift) validates that section injections exist before sensitive operations.

Run your app with Debug configuration (`-Odebug` or `-Onone`) to surface illegal states immediately rather than silently failing. If assertions trigger, the stack trace points directly to unbound sections.

## Track Deferred Scroll Requests

Scroll requests made before the collection view is ready are deferred using `SKRequestID`. When you call `manager.scroll(to:row:animated:)`, if the view lacks a valid frame, the request stores in `afterLayoutSubviewsRequests` and executes after the next `layoutSubviews` cycle.

To verify deferred execution:

1. Call `manager.scroll(to:row:animated:)` early in `viewDidLoad`
2. Set a breakpoint in `perform(of:)` in [`SKCManager.swift`](https://github.com/linhay/sectionkit/blob/main/SKCManager.swift)
3. Confirm the request executes only after `layoutSubviews` completes

## Practical Debugging Example

This comprehensive snippet demonstrates checking frame validity, monitoring layout cycles, and verifying section binding:

```swift
import SectionKit
import SectionUI
import Combine

class DebugViewController: UIViewController {
    private let collectionView = SKCollectionView()
    private var manager: SKCManager { collectionView.manager }
    private var cancellables = Set<AnyCancellable>()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Setup with constraints
        view.addSubview(collectionView)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: view.topAnchor),
            collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
        
        // Monitor layout cycles
        collectionView.requestPublishers.layoutSubviews
            .sink { [weak self] _ in
                guard let self = self else { return }
                print("📐 Frame valid: \(self.collectionView.frame.size)")
                // Check pending requests
                print("⏳ Deferred requests: \(self.manager.afterLayoutSubviewsRequests.count)")
            }
            .store(in: &cancellables)
        
        // Create and bind section
        let section = MySection()
        manager.append(section)
        
        // Force binding verification
        manager.reload()
        
        // Attempt scroll (may be deferred)
        DispatchQueue.main.async {
            _ = self.manager.scroll(to: 0, row: 0, animated: true)
        }
    }
}

class MySection: SKCSectionProtocol {
    // Section implementation
}

```

Key debugging points in this code:
- **Lines 17-24**: Ensure frame is valid before manager processes requests
- **Line 28**: Observes `layoutSubviews` to confirm size availability
- **Line 36**: Appends section, triggering `bind(sections:start:)` injection
- **Line 39**: `reload()` verifies binding through the `security(check:)` path
- **Line 43**: Demonstrates deferred scroll requests via `afterLayoutSubviewsRequests`

## Summary

- **Verify frame validity**: `SKCManager` requires non-zero width/height in `setup(request:)` before processing layout requests.
- **Ensure section binding**: Sections must have non-nil `sectionInjection` after `bind(sections:start:)`; DEBUG assertions catch failures.
- **Check plugins**: Validate `collectSectionLayoutPlugins()` returns expected modes and global `pluginModes` don't conflict.
- **Observe lifecycle**: Use `requestPublishers.layoutSubviews` to confirm when the collection view is ready for updates.
- **Control batch updates**: Toggle `SKCManager.configuration.replaceInsertWithReloadData` to test incremental vs full reloads.
- **Track deferred work**: Scroll requests queue in `afterLayoutSubviewsRequests` until `layoutSubviews` completes.

## Frequently Asked Questions

### Why are my SectionKit sections not displaying any cells?

Sections fail to display when `sectionInjection` remains `nil`, typically because the collection view has a zero frame when sections are added. Verify `collectionView.frame.width > 0` in `viewDidLayoutSubviews` before calling manager methods. According to the source in [`SKCManager.swift`](https://github.com/linhay/sectionkit/blob/main/SKCManager.swift), the `setup(request:)` method silently returns if the frame is invalid.

### How can I force incremental updates instead of full reloadData?

Set `SKCManager.configuration.replaceInsertWithReloadData = false` before performing insertions. This forces the manager to use `performBatchUpdates` via `pick(_:completion:)` rather than falling back to `reloadData`. Use this temporarily when debugging to isolate whether specific sections cause crashes during animated updates.

### Where are scroll requests stored if the collection view isn't ready?

Deferred scroll requests accumulate in `SKCManager.afterLayoutSubviewsRequests`, which is an internal array holding `SKRequestID` objects. These execute automatically after the next `layoutSubviews` cycle completes, as observed via `requestPublishers.layoutSubviews`. Breakpoint in `perform(of:)` to confirm execution timing.

### What triggers the debug assertion failures in SectionKit?

The `security(check:)` method in [`SKCManager.swift`](https://github.com/linhay/sectionkit/blob/main/SKCManager.swift) asserts that `sectionInjection != nil` for each section during sensitive operations. This fires when sections receive UI updates (like reloads) before being properly bound to the manager via `bind(sections:start:)`. Always append sections through manager methods rather than instantiating them in isolation.