How can I implement "copy & pasting" to Finder using NSPasteboard? - swift

I implemented copy and pasting using NSPasteboard and FilePromise within my app. Now I'd like to implement the following workflow.
Copy an item in my app.
let provider = NSFilePromiseProvider(fileType: kUTTypeVideo as String, delegate: self)
Open Finder
Paste the item to Finder using the mouse context menu.
I'm stuck at the step 3. Finder doesn't recognize the items I added to the pasteboard so there's no "Paste Item' menu in the context menu.
Please note that this is not a drag-and-drop. With drag-and-drop, the following delegate is called just fine.
func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, writePromiseTo url: URL, completionHandler: #escaping (Error?) -> Void) {
Could someone give me a pointer?

Related

Get "File Save" to Populate "Open Recent" Menu

It's easy to populate the Open Recent menu following the advice from here
UIDocument.open() automatically adds opened files to the "Open Recents" menu
However, File Save out of the Sandbox is different. I'm using code like this, where I move a file from inside the sandbox out:
let documentPicker = UIDocumentPickerViewController(forExporting: [urlOfFileInSandbox], asCopy: false)
topVC.present(documentPicker, animated: true)
This is great but the file's URL doesn't show up in the "Open Recent" menu. Is there an easy way to do this?
(I can obtain the URL in the UIDocumentPickerDelegate, of course)
The solution is pretty simple. You need a UIDocument subclass that does nothing at all when you open a file URL
/// Call .open to add the fileURL to the recent menu
private class OpenRecentMenuModifier: UIDocument {
/// no-op
override func load(fromContents contents: Any, ofType typeName: String?) throws {
debugPrint("Adding \(fileURL.absoluteString) to Open Recent Menu")
}
}
Just call this after save to pretend that you just opened the file.

macOS Quick Look Preview Extension not calling -viewDidDisappear

I'm implementing a Quick Look Preview extension on macOS that will preview some unsupported audio types.
To be able to stop the audio playback when the user stops previewing, I'm pausing my audio engine in -viewDidDisappear.
However, the problem is that -viewDidDisappear is only called when exiting out of Quick Look in Finder altogether, not when I'm navigating to the next/previous file with the arrow keys.
class PreviewViewController: NSViewController, QLPreviewingController {
func preparePreviewOfFile(at url: URL, completionHandler handler: #escaping (Error?) -> Void) {
player.load(url)
player.play()
}
override func viewDidDisappear() {
// Not called when switching to the next or previous preview in Finder!
player.stop()
}
}
I've tried going through all the usual suspects on both NSViewController (-removeFromParent, -viewWillDisappear, -viewDidDisappear) and it's NSView (-viewDidHide, -viewDidMoveToWindow, -viewDidMoveToSuperview), but without success.
Neither of them are called when I switch to a different file in the Quick Look preview in Finder with the arrow keys (resulting in the audio just continuing to play while I'm Quick Looking some other file).
Anyone knows how to know if my preview view controller has been hidden?

application:openUrl:options not called when opening app from share sheet of another app

I would like to open zip files with my app and configured the following:
Document Types
This leads to the desired appearing of my app in other app's share sheet. But when I tap my app in that share sheet, my app is opened, but the following method in my AppDelegate is never called:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool
It's a iOS 13 application - what am I missing here?
If your app is using scenes, then instead of application(_:open:options:) from UIApplicationDelegate protocol, iOS will call scene(_:openURLContexts:) from UISceneDelegate.
https://developer.apple.com/documentation/uikit/uiscenedelegate/3238059-scene
Also probably it's better to change com.pkware.Zip-archive to com.pkware.zip-archive as it's defined as lowercase in the list of System-Declared UTIs

Cannot quit the app if I cancel the save operation in a NSDocument

I'm subclassing the NSDocument class in order have a specific behavior during the save. I save the project in a folder and I create specific sub-folders with audio files used for this project.
I override the following function save(to:ofType:for:delegate:didSave:contextInfo) but I noticed a weird behaviour. Let's say I've the following implementation:
override func save(to url: URL,
ofType typeName: String,
for saveOperation: NSDocument.SaveOperationType,
delegate: Any?,
didSave didSaveSelector: Selector?,
contextInfo: UnsafeMutableRawPointer?) {
guard let customURL = createCustomUrlFrom(url: url) else { return }
super.save(to: customURL,
ofType: typeName,
for: saveOperation,
delegate: delegate,
didSave: didSaveSelector,
contextInfo: contextInfo)
}
I try to create a custom URL and if I don't manage to do it I cancel the save operation.
Now if I quit the app before saving, the app prompts me to save. If I cannot create the custom URL and I return (before the super.save… call), the quit button or "cmd+q" doesn't work! I've to force quit to close the app.
Anyone see what I did wrong here? Does something is running in the background that prevents me to close the app?
UPDATE
Maybe it comes from the sheet prompted when we quit an edited document. We have the window saying Do you want to save the changes made to the document “Untitled”? with 3 buttons Dont's save, cancel save...
If I click on save, then the project already exists I show a window to let the user replace the project or cancel the save (it's done in the createTargetUrlFrom(url:) func. If the user choose to cancel the app cannot be quit.
So I think about the first window running in background maybe…
I found the solution to this issue!
In fact the application was waiting a reply to the NSTerminateLater. I needed to use the reply(toApplicationShouldTerminate:) function. So I just added NSApp.reply(toApplicationShouldTerminate: false) before returning from the guard statement.

How to get filenames when you drag'n'drop files from Finder on your app

I'm trying to let my NSView accept files which you can drag and drop from Finder.
I've already checked the documentation (https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/DragandDrop/Tasks/acceptingdrags.html#//apple_ref/doc/uid/20000993-BABHHIHC), but the only thing that works is accepting a drop of a String.
As the docs say, I have registered the types which can be dropped
func commonInit(){
let allowedDropTypes = [NSFilenamesPboardType]
registerForDraggedTypes( allowedDropTypes )
Swift.print( registeredDraggedTypes )
}
If I print out the registeredDraggedTypes, I get NSFilenamesPboardType.
I've also implemented draggingEntered(sender)
override func draggingEntered(sender: NSDraggingInfo) -> NSDragOperation {
Swift.print("Dragging entered")
return sender.draggingSourceOperationMask()
}
And prepareForDragOperation(sender) and performDragOperation(sender)
override func prepareForDragOperation(sender: NSDraggingInfo) -> Bool {
Swift.print("Prepare for drag operation")
return true
}
override func performDragOperation(sender: NSDraggingInfo) -> Bool {
Swift.print("Perform for drag operation")
return true
}
If I run my app, I can't drop any files from Finder on my app.
When I add NSStringPboardType to the array of allowed types, I can drag a piece of text on the app. If I then try to add NSPDFPboardType to the array and drag a pdf on the app, it also doesn't work.
I've also tried working with the UTI public.file-url, but to no avail.
Also, app sandboxing is turned off.
I hope somebody can help me out :-)
I found out what the problem was.
My NSView had NSImageViews as subviews and these were blocking the drag operation.
I've added an invisible NSView subview at the top and registered the drag operation on that view. Now everything works fine.
Can you try to allow/add the filetypes you want in the Info plist of your project?
example info plist file
I think you need to define the type of files you allow your application to open/handle, although not 100% sure if this is related to drag/drop. Worth a try at least.