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

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/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/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/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:):

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/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:

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:

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

As implemented in 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 and uses @discardableResult to enable chaining:

@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:

@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/Sources/SectionUI/Protocols/SKCAnySingleTypeSectionProtocol.swift) file provides a forwarding implementation:

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/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/Example/Foundation/SingleTypeSectionViewController.swift), demonstrates practical usage of SectionKit cell actions and selections:

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, enabling exhaustive handling and compile-time safety.
  • Event group architecture: The SKEventGroup class in 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. 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →