This been really getting me confused on how to use Drag and Drop for MOV Files on macOS. Below is the code that I have up and running. But I think the part that confuses the most is how to make a .Mov File into a "Transferable" struct that I can use. Any Help would be great!
struct ContentView: View {
@State private var media: [Data] = []
@State private var isResourcesExpanded: Bool = true
@State private var isConvertedFilesExpanded: Bool = true
var body: some View {
NavigationSplitView {
List {
convertedFiles
}
.listStyle(.sidebar)
} detail: {
dragAndDrop
}
}
private var convertedFiles: some View {
Section(
isExpanded: $isConvertedFilesExpanded,
content: {
VStack(alignment: .leading) {
Text("Test")
Text("test2")
}
},
header: {
HStack() {
Image(systemName: "folder.fill")
.foregroundStyle(.teal)
Text("Converted Files")
}
})
}
private var dragAndDrop: some View {
VStack(spacing: 16) {
Text("Drag .MOV Files")
.font(.largeTitle)
Image(systemName: "film")
.font(.system(size: 60))
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.draggable([Data])
}
}
You should consider using .dropDestination
modifier. The .draggable
API marks a view as the source of a drag operation.
//
// ContentView.swift
// drag&drop
//
// Created by 0x67 on 2025-08-30.
//
import SwiftUI
import UniformTypeIdentifiers
struct ContentView: View {
@State private var media: URL?
@State private var isResourcesExpanded: Bool = true
@State private var isConvertedFilesExpanded: Bool = true
var body: some View {
NavigationSplitView {
List {
convertedFiles
}
.listStyle(.sidebar)
} detail: {
dragAndDrop
}
}
private var convertedFiles: some View {
Section(
isExpanded: $isConvertedFilesExpanded,
content: {
VStack(alignment: .leading) {
Text("Test")
Text("test2")
}
},
header: {
HStack() {
Image(systemName: "folder.fill")
.foregroundStyle(.teal)
Text("Converted Files")
}
})
}
private var dragAndDrop: some View {
VStack(spacing: 16) {
Text("Drag .MOV Files")
.font(.largeTitle)
Image(systemName: "film")
.font(.system(size: 60))
if let media {
Text("Selected: \(media.lastPathComponent)")
.font(.footnote)
.foregroundStyle(.secondary)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.dropDestination(for: URL.self) { items, location in
guard let url = items.first else { return false} /*< we can handle multiple items, for simplicity, we expect only one */
let pathExtension = url.pathExtension.lowercased()
guard pathExtension == "mov" else { return false }
do {
media = try copyIntoDocuments(url)
return true
} catch {
print("Copy failed: \(error)")
return false
}
}
}
}
// MARK: - File copy helper
private func copyIntoDocuments(_ sourceURL: URL) throws -> URL {
let fm = FileManager.default
let docs = fm.urls(for: .documentDirectory, in: .userDomainMask).first!
let dest = docs.appendingPathComponent(sourceURL.lastPathComponent)
if fm.fileExists(atPath: dest.path) {
try fm.removeItem(at: dest)
}
if sourceURL.startAccessingSecurityScopedResource() {
defer { sourceURL.stopAccessingSecurityScopedResource() }
try fm.copyItem(at: sourceURL, to: dest)
} else {
try fm.copyItem(at: sourceURL, to: dest)
}
return dest
}
#Preview {
ContentView()
}