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
flowchart LR
Loader["LoadPhotosOperation"] --> Array["contentArray"]
Array --> Table["NSTableView + Cocoa bindings"]
Table --> Adapter["ViewController data source"]
Adapter --> Provider["FilePromiseProvider"]
Adapter --> Receiver["NSFilePromiseReceiver"]
Provider --> External["Finder / other apps"]
Receiver --> Array
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
classDiagram
ViewController *-- Array : contentArray
ViewController *-- OperationQueue : loaderQueue
ViewController *-- OperationQueue : filePromiseQueue
Array o-- PhotoItem
PhotoItem *-- LoadThumbnailOperation : per load
PhotoItem --> ThumbnailDelegate : weak
FilePromiseProvider o-- Dictionary : row and URL userInfo
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 |