-
Notifications
You must be signed in to change notification settings - Fork 249
[desktop_drop] macOS: robust multi-source drag & drop (file URLs, promises, directories) — 0.7.0 #434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Prefer public.file-url / NSFilenamesPboardType; fallback to NSFilePromiseReceiver - Add fromPromise flag; map directories to DropItemDirectory - Generate security-scoped bookmarks only for paths outside container/tmp - Per-drop unique destination for promises - Guards in Dart for empty bookmarks - Thread-safe collection of drop results
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR significantly enhances the macOS implementation of the desktop_drop plugin to handle diverse drag sources more reliably, adding robust support for file promises, directories, and multi-source scenarios. The changes address incomplete multi-file drags and race conditions while introducing new API features for better UX differentiation.
- Implements comprehensive drag source handling (file URLs, legacy filename arrays, and file promises)
- Adds thread-safe promise reception with per-drop unique destinations
- Introduces
fromPromiseflag and directory detection capabilities
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| pubspec.yaml | Version bump to 0.7.0 and flutter_lints upgrade |
| DesktopDropPlugin.swift | Complete rewrite of drag handling logic with multi-source support and thread safety |
| Package.swift | macOS minimum version bump to 10.13 |
| desktop_drop.podspec | macOS minimum version bump to 10.13 |
| drop_item.dart | Added fromPromise field and comprehensive documentation |
| channel.dart | Enhanced platform communication with directory support and bookmark guards |
| CHANGELOG.md | Documented all new features and changes |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| let base = FileManager.default.temporaryDirectory.appendingPathComponent("Drops", isDirectory: true) | ||
| let formatter = DateFormatter() | ||
| formatter.locale = Locale(identifier: "en_US_POSIX") | ||
| formatter.dateFormat = "yyyyMMdd_HHmmss_SSS'Z'" |
Copilot
AI
Sep 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The date format string 'yyyyMMdd_HHmmss_SSS'Z'' includes a literal 'Z' suffix but doesn't actually format the date in UTC timezone. Either remove the 'Z' or set the formatter's timeZone to UTC to match the format.
| formatter.dateFormat = "yyyyMMdd_HHmmss_SSS'Z'" | |
| formatter.dateFormat = "yyyyMMdd_HHmmss_SSS'Z'" | |
| formatter.timeZone = TimeZone(secondsFromGMT: 0) |
| this.extraAppleBookmark, | ||
| this.fromPromise = false, |
Copilot
AI
Sep 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The DropItem.fromData constructor is missing the extraAppleBookmark parameter in its super() call. This will cause extraAppleBookmark to always be null for items created via fromData, which may not be the intended behavior.
| super.name, | ||
| super.length, | ||
| super.lastModified, | ||
| super.path, |
Copilot
AI
Sep 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The DropItemFile.fromData constructor is missing the extraAppleBookmark parameter. It should pass super.extraAppleBookmark to match the pattern used in other constructors.
| super.path, | |
| super.path, | |
| super.extraAppleBookmark, |
|
LGTM, thanks for the contribution 💙 |
Summary
This PR upgrades the macOS implementation to handle real-world drag sources reliably and predictably:
solves #433
public.file-urlandNSFilenamesPboardType(legacy filename arrays) when present.NSFilePromiseReceiverfor sources like VS Code/Electron that deliver file promises.DropItemDirectory.fromPromiseflag onDropItemso apps can tailor UX when the original path is unavailable.start/stopAccessingSecurityScopedResourcecalls when the bookmark is empty.Version bump: 0.7.0. Minimum macOS set to 10.13 (SwiftPM/Podspec).
Motivation & context
Different apps advertise drag data differently:
Previously, multi-file drags could be incomplete, directory drops weren’t differentiated, and promise handling could yield race conditions and unnecessary bookmark attempts. This PR addresses those issues and codifies behavior so plugin consumers can build robust UX across sources.
What’s changed (highlights)
macOS (Swift)
Drops/<timestamp>destination; safer timestamp format.seen/items).NSNullotherwise.isDirectoryandfromPromisein channel payloads.Dart
DropItemnow hasfromPromiseand documentedextraAppleBookmark.isDirectory→DropItemDirectory.start/stopAccessingSecurityScopedResourceagainst empty bookmarks (no-ops).Meta
pubspec.yaml: version →0.7.0.Package.swift/Podspec: macOS min →10.13.CHANGELOG.mdupdated.API changes for consumers
New:
DropItem.fromPromise(bool).truemeans the item came via a file promise and was written into the plugin’sDrops/<timestamp>folder. The original source path is not available by design.Existing:
DropItem.extraAppleBookmark(Uint8List?) is often null/empty for promise files and non-empty for file URLs outside the app container.Behavior:
DesktopDrop.start/stopAccessingSecurityScopedResourceno-op when bookmark is empty.Typical usage:
Backward compatibility
Drops/<timestamp>(still under tmp); previous behavior used a sharedDropsfolder — this reduces collisions but changes the exact path. Consumers reading from the returnedpathare unaffected.Known limitations (documented)
fromPromise = trueand provides a stable, readable copy path.Testing
Manual matrix
Drops/<timestamp>/...;fromPromise = true; bookmarks empty; bytes readable.DropItemDirectorysurfaced;isDirectory = true.Edge cases