Sample CodemacOSReviewed 2026-07-21View on Apple Developer

Add Functionality to Finder with Action Extensions

At a glance

Item Summary
Purpose Add three Finder actions: uppercase text, create image thumbnails, and remove image transparency.
Architecture A nearly empty host app plus three independent extension targets; small NSImage extensions hold reusable transformations.
Main boundary Finder supplies NSExtensionContext input and each action completes or cancels that request.

Project structure

CustomQuickActions.xcodeproj
├── Main Application/AppDelegate.swift
├── UppercaseAction/ActionRequestHandler.swift
├── ThumbnailAction/ActionRequestHandler.swift
├── RemoveOpacityAction/ActionViewController.swift
└── NSImage Extensions/
    ├── NSImage+Thumbnail.swift
    ├── NSImage+Opaque.swift
    └── NSImage+SavePNG.swift

The folders reflect executable targets, not layers in one process. The two request handlers are headless; the opacity action uses a view controller because it asks for a color.

Overall architecture

Reference code

ThumbnailAction/ActionRequestHandler.swift:13 — the extension entry point consumes the framework-owned request.

    func beginRequest(with context: NSExtensionContext) {
        // ...
    }

Ownership and state

Ownership evidence

RemoveOpacityAction/ActionViewController.swift:43 — output state and synchronization are scoped to one user action.

class ActionViewController: NSViewController {
    // ...
        var outputAttachments: [NSItemProvider] = []
    // ...
}

Finder owns the input context. Each extension invocation owns its local output array and dispatch group. NSItemProvider load handlers defer creation of replacement files until the host requests a representation.

Class and protocol design

Type Role Protocol or base class
Two ActionRequestHandler types Transform text or images without UI NSObject, NSExtensionRequestHandling
ActionViewController Own the color picker and image transformation flow NSViewController
NSImage extensions Thumbnail, opacity, and PNG operations Concrete type extensions; no app protocol
AppDelegate Satisfy the containing app target NSApplicationDelegate

The repeated handler name is safe because each declaration is compiled into a different extension target.

Access control

Boundary Effect and rationale
Most declarations are implicit internal Each handler and helper is visible only inside the target that compiles it; there is no library API.
fileprivate createThumbnail Limits the provider factory to its source file, keeping it out of the extension’s callable surface.
public image helpers Explicitly makes the member exportable wherever that source is compiled, although the sample does not expose a public framework.
Local output variables Per-request state cannot be retained or mutated by another request.

ThumbnailAction/ActionRequestHandler.swift:58 shows the narrow file-scoped helper, while NSImage Extensions/NSImage+Thumbnail.swift:12 shows the wider member declaration.

Logic ownership and placement

Logic Location Why it belongs there
Request validation and completion Each action entry type It is specific to the extension contract.
Asynchronous attachment aggregation Request handler or action controller The owner decides when all providers are ready.
Pixel transformations and PNG writing NSImage extensions Both image actions can reuse focused operations.
Temporary replacement URL creation Image action types Output naming differs by action.

Design patterns

Pattern Evidence Use
App extension UppercaseAction/ActionRequestHandler.swift:10 Lets Finder invoke a target without launching the host UI.
Framework callback ThumbnailAction/ActionRequestHandler.swift:13 AppKit hands request ownership into the extension.
Dispatch barrier RemoveOpacityAction/ActionViewController.swift:45 Completes only after asynchronous attachment loads finish.
Concrete utility extension NSImage Extensions/NSImage+Opaque.swift:10 Keeps transformations out of request orchestration.

Naming conventions

  • Target folders use capability names such as ThumbnailAction and UppercaseAction.
  • Framework entry types use Apple’s expected role names: ActionRequestHandler and ActionViewController.
  • Helper methods state the result or side effect: thumbnailImage, opaqueImage, and savePNGToDisk.

Architecture takeaways

  • Treat each app extension as its own composition root and lifetime.
  • Keep extension-context orchestration close to the entry point and reusable transformations separate.
  • Use a per-request synchronization object; do not make request state global.
  • This sample is framework-protocol-oriented at its boundary, not an abstract protocol-oriented domain design.

Source map

Source Role
UppercaseAction/ActionRequestHandler.swift:13 Text request flow
ThumbnailAction/ActionRequestHandler.swift:58 Lazy thumbnail file provider
RemoveOpacityAction/ActionViewController.swift:22 UI action and image request flow
NSImage Extensions/NSImage+SavePNG.swift:12 Shared file output helper