# How SectionKit Handles Cell Actions and Selections: A Complete Technical Guide

> Learn how SectionKit handles cell actions and selections using a type-safe event system. Discover how UIKit callbacks become SKCCellActionContext objects for seamless integration.

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

---

**SectionKit routes every cell interaction through a type-safe event system where `SKCSingleTypeSection` converts UIKit callbacks into `SKCCellActionContext` objects and dispatches them via `SKEventGroup` to subscribers registered with the `onCellAction` fluent API.**

SectionKit provides a declarative, protocol-oriented architecture for managing UICollectionView sections. Understanding how SectionKit handles cell actions and selections requires examining its core abstraction layer that bridges UIKit delegate callbacks with reactive, type-safe event handling. This system models every interaction—from simple taps to complex display lifecycle events—as enumerated types that travel through a thread-safe event group architecture.

## Core Architecture for Cell Actions

SectionKit abstracts cell interactions through three fundamental components: an enumeration of possible actions, a generic event dispatch container, and a typed context object that carries interaction metadata.

### SKCCellActionType Enumeration

The [[`SKCCellActionType.swift`](https://github.com/linhay/sectionkit/blob/main/SKCCellActionType.swift)](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/CollectionBaseProtocol/Models/SKCCellActionType.swift) file defines a comprehensive enumeration of all possible cell-level events. This includes selections, deselections, display lifecycle events, and configuration callbacks. By codifying interactions as enum cases rather than string identifiers, SectionKit enables exhaustive switch statements and compile-time safety when handling SectionKit cell actions and selections.

### SKEventGroup Event Dispatch System

The generic `SKEventGroup<Event, Element>` class, implemented in [[`SKEventGroup.swift`](https://github.com/linhay/sectionkit/blob/main/SKEventGroup.swift)](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/Common/SKEventGroup.swift), serves as a thread-safe container that stores an array of handlers per event key. When an action occurs, the system iterates sequentially through all registered callbacks for that specific event type. This design decouples the UIKit delegate methods from the business logic that responds to them.

### Action Context Construction

When a UI event occurs, SectionKit constructs an `SKCCellActionContext<Cell>` (defined inline within [[`SKCSingleTypeSection.swift`](https://github.com/linhay/sectionkit/blob/main/SKCSingleTypeSection.swift)](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/CollectionSingleTypeSection/Entities/SKCSingleTypeSection.swift)). This context encapsulates:
- A reference to the originating section
- The `SKCCellActionType` action type
- The data model at the corresponding row
- The row index
- An optional weak reference to the cell view

## The Action Dispatch Flow

The path from a user tapping a cell to executing your business logic follows a strict four-phase pipeline implemented in the concrete section classes.

### From UIKit Callback to Section Method

When the collection view reports a selection, the `SKCSingleTypeSection` receives the delegate callback and immediately delegates to its internal handler. For selection events, the system invokes `item(selected:)`:

```swift
open func item(selected row: Int) {
    sendAction(.selected, view: nil, row: row)
}

```

This method appears at lines 217–219 in [[`SKCSingleTypeSection.swift`](https://github.com/linhay/sectionkit/blob/main/SKCSingleTypeSection.swift)](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/CollectionSingleTypeSection/Entities/SKCSingleTypeSection.swift), demonstrating how SectionKit intercepts UIKit delegate methods and normalizes them into internal action types.

### Context Generation and Validation

The `sendAction(_:view:row:)` method validates the row index against the current models array and constructs the typed context. Lines 804–807 in the same file perform this construction:

```swift
let result = SKCCellActionContext<Cell>(section: self,
                                        type: type,
                                        model: models[row],
                                        row: row,
                                        _view: view)

```

This validation ensures that actions cannot fire for rows that no longer exist in the data model, preventing crashes during rapid updates or animations.

### Event Group Distribution

Once constructed, the context enters the distribution phase. The section iterates through all closures stored in the `cellActions` event group for the specific action type:

```swift
for block in cellActions[result.type] {
    block(result)
}
publishers.cellActionSubject?.send(result)

```

As implemented in [`SKCSingleTypeSection+cellStyle.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/CollectionSingleTypeSection/Entities/SKCSingleTypeSection+cellStyle.swift), this dual-dispatch pattern ensures both immediate closure execution and reactive stream publication occur synchronously.

### Combine Publisher Integration

Beyond closure-based handlers, SectionKit exposes a Combine-style publisher (`cellActionSubject`) that emits every action context. This enables reactive programming patterns where multiple observers can subscribe to selection events without directly registering closures on the section instance.

## Registering Cell Action Handlers

SectionKit provides a fluent, chainable API for subscribing to cell actions that emphasizes type safety and memory management.

### The onCellAction Fluent API

The primary registration method appears in [`SKCSingleTypeSection+cellStyle.swift`](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/CollectionSingleTypeSection/Entities/SKCSingleTypeSection+cellStyle.swift) and uses `@discardableResult` to enable chaining:

```swift
@discardableResult
func onCellAction(_ kind: SKCCellActionType,
                  block: @escaping CellActionBlock) -> Self {
    cellActions.append(of: kind, block)
    return self
}

```

This design allows developers to register multiple handlers in a single fluent expression while maintaining references to the section instance for further configuration.

### Weak Reference Overloads for Memory Safety

To prevent retain cycles when the action handler needs to reference a view controller or other parent object, SectionKit provides a weak-reference overload:

```swift
@discardableResult
func onCellAction<ON: AnyObject>(on: ON,
                                  _ kind: SKCCellActionType,
                                  block: @escaping CellActionWeakBlock<ON>) -> Self {
    return onCellAction(kind) { [weak on] context in
        guard let on = on else { return }
        block(on, context)
    }
}

```

Located at lines 12–18 in the cell style extension, this overload automatically manages weak capture of the owner object, eliminating manual memory management boilerplate.

### Protocol-Level Abstractions

For type-erased sections conforming to `SKCAnySingleTypeSectionProtocol`, the [[`SKCAnySingleTypeSectionProtocol.swift`](https://github.com/linhay/sectionkit/blob/main/SKCAnySingleTypeSectionProtocol.swift)](https://github.com/linhay/sectionkit/blob/main/Sources/SectionUI/Protocols/SKCAnySingleTypeSectionProtocol.swift) file provides a forwarding implementation:

```swift
func onCellAction(_ kind: SKCCellActionType,
                  block: @escaping RawSection.CellActionBlock) -> Self {
    rawSection.onCellAction(kind, block: block)
    return self
}

```

This ensures that whether you work with concrete `SKCSingleTypeSection` instances or protocol-oriented abstractions, the API surface remains consistent.

## Handling Different Interaction Types

SectionKit categorizes cell interactions into distinct families, each following the same dispatch pattern but originating from different UIKit delegate methods.

### Selection and Deselection

The most common cell actions involve user selection. When a user taps a cell, UIKit calls `collectionView(_:didSelectItemAt:)`, which SectionKit routes to `item(selected:)` and dispatches as `.selected`. The corresponding deselection uses `.deselected`. These actions are defined in [[`SKCCellActionType.swift`](https://github.com/linhay/sectionkit/blob/main/SKCCellActionType.swift)](https://github.com/linhay/sectionkit/blob/main/Sources/SectionKit/CollectionBaseProtocol/Models/SKCCellActionType.swift) and handled through the standard event group pipeline.

### Display Lifecycle Events

Beyond user interactions, SectionKit treats display lifecycle events as first-class actions. The `.willDisplay` and `.didEndDisplay` types fire when cells enter or exit the visible viewport, allowing for lazy loading, animation triggers, or resource cleanup. These originate from UIKit's `collectionView(_:willDisplay:forItemAt:)` and `collectionView(_:didEndDisplaying:forItemAt:)` callbacks.

### Primary Actions and Highlights

Modern iOS versions support primary actions (iOS 16+) and highlight states. SectionKit exposes these through `item(canPerformPrimaryAction:)` and `item(performPrimaryAction:)` for primary actions, plus `item(shouldHighlight:)`, `item(didHighlight:)`, and `item(didUnhighlight:)` for selection highlighting. All follow the identical pattern: UIKit delegate method → context generation → event group dispatch.

## Complete Implementation Example

The following example, adapted from [[`SingleTypeSectionViewController.swift`](https://github.com/linhay/sectionkit/blob/main/SingleTypeSectionViewController.swift)](https://github.com/linhay/sectionkit/blob/main/Example/Foundation/SingleTypeSectionViewController.swift), demonstrates practical usage of SectionKit cell actions and selections:

```swift
let section = DemoCell
    .wrapperToSingleTypeSection([.blue, .green, .red])
    .onCellAction(.selected) { context in
        // context.view() lazily returns the cell if it still exists
        context.view()?.contentView.backgroundColor = .purple
    }
    .onCellAction(.deselected) { context in
        context.view()?.contentView.backgroundColor = .clear
    }

manager.reload(section)

```

When the user taps a cell, the collection view triggers the internal `item(selected:)` method, which creates a `SKCCellActionContext` with `type == .selected`. The registered closure receives this context, providing access to the cell, the underlying model, and the section instance without requiring manual index path translation or optional casting.

## Summary

- **Type-safe enumeration**: All cell interactions are modeled as `SKCCellActionType` cases defined in [`SKCCellActionType.swift`](https://github.com/linhay/sectionkit/blob/main/SKCCellActionType.swift), enabling exhaustive handling and compile-time safety.
- **Event group architecture**: The `SKEventGroup` class in [`SKEventGroup.swift`](https://github.com/linhay/sectionkit/blob/main/SKEventGroup.swift) manages thread-safe storage and sequential invocation of action handlers.
- **Context propagation**: `SKCSingleTypeSection` creates `SKCCellActionContext` instances that encapsulate the cell, model, and metadata, then dispatches them through both closure-based handlers and Combine publishers.
- **Fluent registration**: The `onCellAction` API in `SKCSingleTypeSection+cellStyle.swift` provides chainable, type-safe subscription with built-in weak-reference overloads to prevent memory leaks.
- **Protocol consistency**: `SKCAnySingleTypeSectionProtocol` ensures the same API works for both concrete sections and type-erased wrappers.

## Frequently Asked Questions

### How does SectionKit prevent memory leaks when subscribing to cell actions?

SectionKit provides a weak-reference overload of `onCellAction` that accepts an owner object and a closure taking both the owner and the context. According to the implementation in `SKCSingleTypeSection+cellStyle.swift` (lines 12–18), this overload automatically captures the owner as a weak reference and guards its existence before executing the block, eliminating retain cycles between the section and view controllers.

### What is the difference between cell actions and selection handling in SectionKit?

**Selection handling** refers specifically to the `.selected` and `.deselected` action types that fire when users tap or untap cells. **Cell actions** encompass a broader category including selection, display lifecycle events (`.willDisplay`, `.didEndDisplay`), and configuration events. All follow the same dispatch mechanism through `SKEventGroup`, but selection is merely one specific enum case within the comprehensive `SKCCellActionType` system.

### Can I use Combine publishers instead of closure-based handlers?

Yes. Every action context emitted through the `cellActions` event group is simultaneously forwarded to `publishers.cellActionSubject`, as shown in lines 10–14 of [`SKCSingleTypeSection.swift`](https://github.com/linhay/sectionkit/blob/main/SKCSingleTypeSection.swift). This allows you to subscribe to the publisher using standard Combine operators like `.sink` or `.assign`, enabling reactive patterns where multiple observers react to the same selection event without direct closure registration.

### Where is the cell view actually stored in the action context?

The cell view is stored as an optional weak reference within the `SKCCellActionContext` structure. When you access `context.view()`, the implementation performs a lazy lookup to return the cell only if it still exists in the collection view's visible cells. This design prevents the context from retaining the cell (which would create a retain cycle) while still allowing handlers to access the view for UI updates when appropriate.