SwiftUI拖放功能在Mac上无法处理所有文件的public.data问题
拖放文件功能失效问题排查与修复
我正在开发SwiftUI应用,需要支持任意文件拖放并显示MIME类型,同时用NSOpenPanel做备选文件选择(该功能运行正常),但拖放功能处理所有文件均失败。
问题代码(HandleDrop)
private func handleDrop(providers: [NSItemProvider]) -> Bool { var success = false for provider in providers { print("Checking provider: \(provider)") // Handle public.file-url if provider.hasItemConformingToTypeIdentifier("public.file-url") { print("Provider conforms to public.file-url") provider.loadItem(forTypeIdentifier: "public.file-url", options: nil) { item, error in if let url = item as? URL { print("Dropped file URL: \(url)") DispatchQueue.main.async { droppedFileType = "MIME Type: \(url.mimeType ?? "Unknown")" } success = true } else { print("Failed to cast item to URL for public.file-url") } } } // Handle public.data else if provider.hasItemConformingToTypeIdentifier("public.data") { print("Provider conforms to public.data") provider.loadItem(forTypeIdentifier: "public.data", options: nil) { item, error in if let data = item as? Data { let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString).appendingPathExtension("pdf") do { try data.write(to: tempURL) print("Saved file to temporary URL: \(tempURL)") DispatchQueue.main.async { droppedFileType = "MIME Type: \(tempURL.mimeType ?? "Unknown")" } success = true } catch { print("Failed to write data to temporary file") } } else { print("Failed to decode item as public.data") } } } // Unsupported type else { print("Provider does NOT conform to public.file-url or public.data") } } return success }
现象说明
NSOpenPanel对所有文件类型都能正常工作,可正确检测并显示MIME类型。但拖放功能的provider虽符合public.data,却无法解码为Data,日志显示“Failed to decode item as public.data”,测试.png、.txt等文件均失败。
当前权限配置(.entitlement)
<key>com.apple.security.app-sandbox</key> <true/> <key>com.apple.security.files.user-selected.read-write</key> <true/> <key>com.apple.security.files.downloads.read-write</key> <true/>
修复方案
1. 修正public.data的解码逻辑
public.data类型的item不一定直接是Swift的Data,可能是NSData或NSSecureCoding兼容对象,需要补充类型转换:
修改public.data分支的代码:
else if provider.hasItemConformingToTypeIdentifier("public.data") { print("Provider conforms to public.data") provider.loadItem(forTypeIdentifier: "public.data", options: nil) { item, error in guard let dataObj = item as? NSSecureCoding else { print("Failed to decode item as public.data") return } do { let data = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSData.self, from: dataObj as! Data) ?? NSData() let swiftData = Data(referencing: data) // 获取原文件扩展名(从provider的UTI转换) let ext = provider.registeredTypeIdentifiers.compactMap { UTType($0)?.preferredFilenameExtension }.first ?? "tmp" let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString).appendingPathExtension(ext) try swiftData.write(to: tempURL) print("Saved file to temporary URL: \(tempURL)") DispatchQueue.main.async { droppedFileType = "MIME Type: \(tempURL.mimeType ?? "Unknown")" } success = true } catch { print("Failed to process public.data: \(error)") } } }
2. 补充沙箱权限
拖放文件可能来自沙箱未授权的位置,需要添加书签权限以安全访问:
在.entitlement中添加:
<key>com.apple.security.files.bookmarks.app-scope</key> <true/>
同时,处理public.file-url时需要创建安全书签才能访问文件:
if provider.hasItemConformingToTypeIdentifier("public.file-url") { print("Provider conforms to public.file-url") provider.loadItem(forTypeIdentifier: "public.file-url", options: nil) { item, error in if let url = item as? URL { do { let bookmark = try url.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil) guard let securedURL = URL(resolvingBookmarkData: bookmark, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: nil) else { print("Failed to resolve secured URL") return } securedURL.startAccessingSecurityScopedResource() defer { securedURL.stopAccessingSecurityScopedResource() } print("Dropped file URL (secured): \(securedURL)") DispatchQueue.main.async { droppedFileType = "MIME Type: \(securedURL.mimeType ?? "Unknown")" } success = true } catch { print("Failed to handle secure URL: \(error)") } } else { print("Failed to cast item to URL for public.file-url") } } }
3. 修复临时文件扩展名硬编码问题
原代码中临时文件固定为.pdf,会导致非PDF文件的MIME类型检测错误,改为从provider的UTI获取对应扩展名。
内容的提问来源于stack exchange,提问作者esafwan
相关产品推荐
相关产品推荐

