Reputation: 6046
I have set up a SwiftUI app that seems to accept images dropped onto the dock icon, but I cannot figure out where to handle the dropped image in my app code.
How can I handle dropping an image (or any particular file) onto the dock icon of a SwiftUI app?
With older-style Swift code that used NSApplication, handling file drops on an application's dock icon could be done in two steps:
This is documented succinctly in a separate question.
SwiftUI apps do not provide an app delegate by default, though. To implement these functions, you must do some additional work:
Create a class that implements NSObject
and NSApplicationDelegate
(or UIApplicationDelegate
):
// or NSApplicationDelegate
class AppDelegate: NSObject, UIApplicationDelegate {
// ...
}
In your @main
App
implementation, setup the delegate:
... : App {
// or @NSApplicationDelegateAdaptor
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
You can now implement app delegate methods! For example, this will print when your app launches:
func applicationWillFinishLaunching(_ notification: Notification) {
print("App Delegate loaded!")
}
But implementing the openFile functions doesn't work:
func application(_ sender: NSApplication, openFile filename: String) -> Bool {
print("test", filename)
return false
}
func application(_ sender: NSApplication, openFiles filenames: [String]) {
print("another test", filenames)
}
Neither of these print out when dragging a file onto the app.
This seems to be a result of some work to separate AppDelegate functionality into SceneDelegates:
For those who might bump their heads in this, the equivalent functionality gets called in the scenedelegate now due to the separation of appdelegate’s functionality. The equivalent-ish function is scene(_ scene: openURLContexts:). I haven’t researched if it’s possible to ‘opt out’ of this, but for my purposes there’s no reason not to adopt the new behavior
— m_bedwell on application(open: options:) not being called (emphasis added)
But there is no obvious way to access a SceneDelegate for our code (which may not even be applicable on macOS?). There is a promising similar question.
Is there a better way?
Upvotes: 4
Views: 1038
Reputation: 6046
There's a very easy way to handle drops onto the Dock icon in SwiftUI!
Update your Info.plist to indicate your app supports the file types you want (as you would in an older app):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- ... -->
<key>CFBundleTypeName</key>
<string>Image files</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Default</string>
<key>LSItemContentTypes</key>
<array>
<string>public.image</string>
</array>
</dict>
</plist>
In your body
in your App
, use onOpenURL:
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { (url) in
// Handle url here
}
}
}
— ianivs (How to handle URL callbacks with new SwiftUI @main startup?)
Upvotes: 5