Sample CodeiOS, iPadOS, Mac CatalystReviewed 2026-07-21View on Apple Developer

Adding context menus in your app

At a glance

Item Summary
Purpose Demonstrates context-menu actions, submenus, previews, targeted previews, and commits across collections, tables, controls, views, and web content.
App architecture A storyboard-driven example matrix shares base list controllers and protocol-extension action factories; each concrete screen owns one UIKit integration variant.
Main patterns Protocol extension, template superclass, framework delegate, diffable data source, and split-view coordinator.
Project style One UIKit target with many small *Basic, *Submenu, *Preview, and *PreviewProvider screens.

Project structure

ContextMenus/
├── ContextMenu.swift
├── Model.swift
├── BaseCollectionViewTestViewController.swift
├── BaseTableTestViewController.swift
├── CollectionView*.swift
├── TableView*.swift
├── Control*.swift
├── View*.swift
├── WebView*.swift
├── DetailViewManager.swift
├── SceneDelegate.swift
└── Base.lproj/Main.storyboard

Structure observations

  • Filename families form a deliberate matrix of host (CollectionView, TableView, Control, View, WebView) and behavior (Basic, Submenu, Preview, PreviewProvider).
  • Base list controllers own common sample data and layout; subclasses override only context-menu callbacks.
  • Shared menu-action construction lives in capability protocols, not a utility singleton.

Overall architecture

Reference code

ContextMenus/CollectionViewPreviewProvider.swift:14 — one example combines shared list data, a custom preview provider, and protocol-built actions.

override func collectionView(_ collectionView: UICollectionView,
                             contextMenuConfigurationForItemAt indexPath: IndexPath,
                             point: CGPoint) -> UIContextMenuConfiguration? {
    guard let item = dataSource.itemIdentifier(for: indexPath) else { return nil }
    return UIContextMenuConfiguration(identifier: NSString(string: item.name),
        previewProvider: { self.storyboard?.instantiateViewController(identifier: "PreviewViewController") },
        actionProvider: { _ in
            UIMenu(children: [self.inspectAction(indexPath),
                              self.duplicateAction(indexPath),
                              self.deleteAction(indexPath)])
        })
}

Interpretation

This is a catalog architecture, not one feature pipeline. Each screen is intentionally concrete, while base controllers and capability extensions remove only the repeated data/layout and action-building mechanics.

Ownership and state

Ownership evidence

ContextMenus/SceneDelegate.swift:25 — the scene creates and retains the split-view helper, then injects the storyboard-owned split controller.

func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
           options connectionOptions: UIScene.ConnectionOptions) {
    if let splitViewController = window!.rootViewController as? UISplitViewController {
        detailViewManager = DetailViewManager()
        detailViewManager.splitViewController = splitViewController
    }
}
Owner Object or state Relationship Mutation authority
Scene delegate DetailViewManager Creates and strongly retains Scene setup only
Detail manager Split view and KVO observation Receives split view, installs itself as delegate Manager callbacks
Base list controller Diffable data source and initial snapshot Creates and retains Base and subclasses through internal property
Concrete example screen Configuration closures and preview controllers Creates per interaction Framework callback implementation
Host view/control UIContextMenuInteraction Retains after addInteraction UIKit lifecycle

Class and protocol design

Type or protocol Responsibility Depends on
ContextMenu Require inspect/duplicate/delete commands without an index path Concrete adopter behavior
IndexPathContextMenu Same capability for list items IndexPath and adopter behavior
WebViewContextMenu Add one URL-specific action URL and adopter behavior
Protocol extensions Build reusable UIAction values that call required methods UIKit menu types
Base list controllers Configure layout, diffable source, and demo items Model, reusable cells
Concrete example controllers Implement one context-menu configuration/preview style UIKit or WebKit delegate APIs
DetailViewManager Keep split-view detail navigation coherent during size changes UISplitViewControllerDelegate, KVO

This is genuine protocol-oriented reuse: protocols describe commands, extensions provide action factories, and concrete screens retain control of feature behavior.

Access control

Symbol Access Verified effect Likely rationale
sectionInsets private Collection subclasses cannot modify base layout spacing. Protect base-controller layout details.
Base dataSource implicit internal Concrete subclasses in other files can look up item identifiers. Required subclass collaboration across files.
makeTargetedPreview helpers private Preview geometry stays inside each example screen. Avoid turning one demonstration’s geometry into shared API.
DetailViewManager.viewControllersObserver private Only the manager owns observation lifetime. Keep KVO cleanup/state local.
Capability protocols and example types implicit internal Reusable within the app target, not exported. The sample is a catalog application.

Reference code

ContextMenus/BaseCollectionViewTestViewController.swift:10 — common layout is private, while the data source is intentionally visible to subclasses.

class BaseCollectionViewTestViewController: UICollectionViewController {
    private let sectionInsets = UIEdgeInsets(top: 6, left: 6, bottom: 6, right: 6)
    enum Section { case main }
    var dataSource: UICollectionViewDiffableDataSource<Section, Model>! = nil
}

Logic ownership and placement

Logic Owning type or file Placement rationale
Common action construction ContextMenu.swift protocol extensions Shared behavior depends only on a small capability.
Shared list layout/data setup Base table/collection controllers Template behavior for many examples.
Configuration, preview, commit behavior Each concrete example controller Keeps each API variation readable in isolation.
Web-element context menus WebViewPreviewProvider WebKit delegate boundary owns URL extraction and fallback preview.
Split-detail replacement DetailViewManager Requires whole split-view context, separate from menu examples.

Design patterns

Pattern Source evidence Purpose or tradeoff
Protocol extension ContextMenus/ContextMenu.swift:10 Shares action factories while forcing adopters to supply effects.
Template superclass ContextMenus/BaseCollectionViewTestViewController.swift:10 Removes repeated list setup; subclasses depend on inherited mutable data source.
Example matrix ContextMenus/ControlBasic.swift:10 and neighboring families Makes API variations easy to compare at the cost of many small controllers.
Framework delegate ContextMenus/WebViewPreviewProvider.swift:32 Adapts UIKit/WebKit callbacks to configuration values and commits.
Diffable data source ContextMenus/BaseCollectionViewTestViewController.swift:27 Gives sample items stable identity for list interactions.

Naming conventions

  • Family prefixes name the host surface; suffixes name the demonstrated behavior.
  • Base...TestViewController signals reusable catalog scaffolding rather than production domain screens.
  • Capability protocols use noun phrases; required methods use perform..., and factories use ...Action.
  • PreviewProvider names controllers that supply preview closures, not standalone provider objects.

Architecture takeaways

  • Use capability protocols plus extensions when many unrelated screens share action construction but not effects.
  • Keep UIKit context-menu configuration with the host controller because identifiers, previews, and commit navigation are host-specific.
  • Share only the stable scaffolding—data source and layout—through base controllers.
  • Distinguish framework ownership after addInteraction from temporary local creation.

Source map

Source file Relevant symbols
ContextMenus/ContextMenu.swift Three action capability protocols and default factories
ContextMenus/BaseCollectionViewTestViewController.swift Shared collection setup
ContextMenus/BaseTableTestViewController.swift Shared table setup
ContextMenus/CollectionViewPreviewProvider.swift List preview-provider example
ContextMenus/ControlBasic.swift Interaction and button-menu example
ContextMenus/WebViewPreviewProvider.swift WebKit context menu and preview fallback
ContextMenus/DetailViewManager.swift Split-view detail coordination