.claude/skills/tooling/macos-dnd/SKILL.md
macOS drag-and-drop patterns using Transferable, onDrag/onDrop, and dropDestination. Use when implementing list reordering, cross-app drops, or file drops.
npx skillsauth add brdohman/agile-maestro macos-dndInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The modern approach. Conform your types to Transferable:
struct Account: Identifiable, Codable, Transferable {
let id: UUID
var name: String
var balance: Decimal
static var transferRepresentation: some TransferRepresentation {
CodableRepresentation(contentType: .account)
}
}
// Define custom UTType
extension UTType {
static var account = UTType(exportedAs: "com.myapp.account")
}
struct AccountListView: View {
@State private var accounts: [Account] = []
var body: some View {
List {
ForEach(accounts) { account in
AccountRow(account: account)
}
.onMove { source, destination in
accounts.move(fromOffsets: source, toOffset: destination)
}
}
}
}
.onMove gives you drag-to-reorder for free in List with ForEach.
AccountRow(account: account)
.draggable(account) // Requires Transferable conformance
Or with a custom preview:
.draggable(account) {
Label(account.name, systemImage: "building.columns")
.padding(8)
.background(.regularMaterial, in: RoundedRectangle(cornerRadius: 8))
}
List {
ForEach(categories) { category in
CategoryRow(category: category)
.dropDestination(for: Account.self) { accounts, _ in
moveAccounts(accounts, to: category)
return true
}
}
}
.onDrop(of: [.account, .fileURL], isTargeted: $isTargeted) { providers in
for provider in providers {
if provider.hasItemConformingToTypeIdentifier(UTType.fileURL.identifier) {
_ = provider.loadObject(ofClass: URL.self) { url, _ in
guard let url else { return }
Task { @MainActor in
importFile(at: url)
}
}
}
}
return true
}
Accept files dragged from Finder:
struct ImportDropZone: View {
@State private var isTargeted = false
var body: some View {
ContentUnavailableView("Drop Files Here", systemImage: "arrow.down.doc")
.dropDestination(for: URL.self) { urls, _ in
let csvFiles = urls.filter { $0.pathExtension == "csv" }
guard !csvFiles.isEmpty else { return false }
importCSVFiles(csvFiles)
return true
} isTargeted: { targeted in
isTargeted = targeted
}
.border(isTargeted ? Color.accentColor : .clear, width: 2)
}
}
For dragging data out of your app to other apps:
struct TransactionRow: View {
let transaction: Transaction
var body: some View {
HStack { /* ... */ }
.draggable(transaction.csvLine) // String is Transferable by default
}
}
Built-in Transferable types: String, Data, URL, Image, Color, AttributedString.
Offer multiple formats so different drop targets can pick what they support:
struct Transaction: Transferable {
static var transferRepresentation: some TransferRepresentation {
CodableRepresentation(contentType: .transaction)
ProxyRepresentation(exporting: \.csvLine) // Plain text fallback
FileRepresentation(exportedContentType: .commaSeparatedText) { transaction in
let data = transaction.csvLine.data(using: .utf8)!
let url = FileManager.default.temporaryDirectory.appendingPathComponent("export.csv")
try data.write(to: url)
return SentTransferredFile(url)
}
}
}
Drag-and-drop must have keyboard alternatives:
.onMove + Edit mode (system provides Move Up/Down in accessibility)Transferable requires macOS 13+. For older targets, use NSItemProvider directly..onMove only works inside ForEach within a List. Not in LazyVStack or ScrollView.dropDestination replaces content by default. Use onDrop if you need append behavior.url.startAccessingSecurityScopedResource() if needed.isTargeted feedback so users know where they can drop.testing
XCTest patterns for macOS Swift apps. Unit tests, async tests, Core Data tests, mock patterns, and assertion reference. Use when writing or reviewing tests.
tools
How to transition workflow state between review stages. Rules for setting review_stage and review_result fields on Stories and Epics.
documentation
Comment structure and rules for task workflow updates. Use when adding any comment to a task during implementation, review, or fix cycles.
testing
Validate task/story/epic/bug/techdebt metadata against schema v2.0. Run after TaskCreate or TaskUpdate to verify compliance. Returns pass/fail with actionable details.