Sample CodemacOSReviewed 2026-07-21View on Apple Developer

Supporting Table View Drag and Drop Through File Promises

At a glance

Item Summary
Purpose Load photos into a table, reorder rows, receive external images/promises, and promise files to other apps.
Architecture A view controller owns the bound content array and operation queues; model objects lazily load thumbnails; a capability extension implements table and promise protocols.
Table model @objc dynamic contentArray is the Cocoa-binding and row-order source of truth.

Project structure

TableViewDragDrop/
├── ViewController.swift
├── ViewController+DragAndDrop.swift
├── PhotoItem.swift
├── TableCellView.swift
├── LoadPhotosOperation.swift
├── LoadThumbnailOperation.swift
└── PromiseFileProvider.swift

This is the table-view counterpart to the collection sample, with binding-friendly model properties and row numbers replacing snapshots and index paths.

Overall architecture

Reference code

TableViewDragDrop/ViewController.swift:18 — the controller’s array is deliberately exposed to Objective-C KVC for storyboard bindings.

/** This contentArray need @objc to make it key value compliant with this view controller,
    and so they are accessible and usable with Cocoa bindings.
*/
@objc dynamic var contentArray = [PhotoItem]()

Ownership and state

Ownership evidence

TableViewDragDrop/PhotoItem.swift:20 — each model owns its file/display state and guards its own asynchronous load.

class PhotoItem: NSObject {
    // ...
}

The view controller owns ordering, UI, import destination, and I/O queues. PhotoItem owns per-file and thumbnail state. The provider carries a row plus URL for drag representations; the controller resolves that row when the destination requests the file.

Class and protocol design

Type Responsibility
ViewController Bound table content, queue ownership, initial loading, and thumbnail refresh.
ViewController drag/drop extension NSTableViewDataSource and NSFilePromiseProviderDelegate behavior.
PhotoItem KVC-visible row model and lazy thumbnail state.
TableCellView Row presentation and image loading trigger.
Operation subclasses Discover photos and derive thumbnails.
FilePromiseProvider Add row and file representations to the pasteboard.

Access control

Boundary Effect and rationale
private imageLoading Makes one PhotoItem the authority preventing duplicate loads.
private cell presentation details Keeps row-view internals out of controller logic.
weak thumbnailDelegate Avoids model-to-controller ownership.
@objc dynamic properties Enable Cocoa bindings/KVO but remain implicit internal, not public API.
public override writingOptions Matches the superclass override surface; the internal subclass caps external use.

TableViewDragDrop/PromiseFileProvider.swift:63 is the explicit public override; TableViewDragDrop/PhotoItem.swift:30 is the private load guard.

Logic ownership and placement

Logic Owner
Bound row ordering and UI refresh ViewController
Drop validation, row moves, imports, and promised writes ViewController+DragAndDrop
Pasteboard type/property-list translation FilePromiseProvider
Lazy thumbnail lifecycle PhotoItem plus LoadThumbnailOperation
Row rendering TableCellView

Design patterns

Pattern Evidence Purpose
MVC TableViewDragDrop/PhotoItem.swift:20, TableViewDragDrop/ViewController.swift:14 Separates row state from table coordination.
Cocoa binding TableViewDragDrop/ViewController.swift:21 Lets the storyboard observe the content array.
Operation TableViewDragDrop/LoadPhotosOperation.swift:12 Moves file discovery off the main thread.
Delegate TableViewDragDrop/PhotoItem.swift:16 Reports thumbnail completion to the table owner.
File promise TableViewDragDrop/ViewController+DragAndDrop.swift:382 Defers file output until drop time.

Naming conventions

  • Table-specific identity is explicit: rowDragType, rowNumberKey, and TableCellView.
  • Background workers use Load...Operation; the displayed domain object is PhotoItem.
  • ViewController+DragAndDrop signals where protocol-heavy integration lives.

Architecture takeaways

  • Make one array authoritative for table order and promise row lookup.
  • Distinguish KVC visibility from public module access.
  • Keep slow work on operation queues and marshal UI mutations to the main queue.
  • Put table policy in its data-source extension and representation policy in the provider.

Source map

Source Role
TableViewDragDrop/ViewController.swift:14 Table state owner
TableViewDragDrop/ViewController+DragAndDrop.swift:13 Drag/drop data source
TableViewDragDrop/PhotoItem.swift:20 Bound photo model
TableViewDragDrop/PromiseFileProvider.swift:10 Pasteboard provider
TableViewDragDrop/TableCellView.swift:10 Row presentation