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

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.

Related

Opening a file from SafariWebExtensionHandler

I'm developing a Safari Web Extension. Among other things it has to open files on the user's disc (in their corresponding apps, like "Preview" app for png file and so on) when user clicks a browser contextual menu item.
But if I try to do something like this (for testing purposes):
class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling {
func beginRequest(with context: NSExtensionContext) {
NSWorkspace.shared.open(URL(fileURLWithPath: "/Users/username/Downloads/banner.png"))
}
}
I get the following error:
The application “(null)” does not have permission to open “banner.png.”
Which is weird, because this works fine:
NSWorkspace.shared.selectFile("/Users/username/Downloads/banner.png", inFileViewerRootedAtPath: "/Users/username/Downloads")
Will be grateful for any suggestions
I tried calling shell script to open the file, but it doesn't work either.

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

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?

NSSavePannel - how to restrict user to only save one one set directory?

User has to save a file, but I only want them saving the file in one folder. How to do this?
I have already tried implementing the delegate and forcefully setting back the directory if it is different. This does not work. The user is still able to select other folders when the save panel opens
extension Project: NSOpenSavePanelDelegate {
func panel(_ sender: Any, didChangeToDirectoryURL url: URL?) {
if url != testsFolder {
(sender as! NSSavePanel).directoryURL = testsFolder
}
}
func panel(_ sender: Any, validate url: URL) throws {
if url.deletingLastPathComponent() != testsFolder {
(sender as! NSSavePanel).directoryURL = testsFolder
throw ProjectError.scriptInitiliation
}
}
}
The thing is, the folder is already fix fixed within the app.
This is the time to acquire permission from the user to access this folder. Use a open (not save) dialog to have the user confirm selection of the folder. Think of this as a "confirm access dialog", you can:
Change the label of the "Open" button to something else using prompt
Set the title and message so the dialog is clearly a confirmation dialog
set the initial folder using directoryURL to the parent of the one you want confirmed (Note: any changes to directoryURL after the dialog is up are ignored so you cannot lock the dialog to that folder using the delegate didChangeToDirectoryURL – in the early sandbox you could but Apple has now stopped that)
Set the delegate and use its shouldEnable and validate callbacks to make sure only the folder you wish to have confirmed can be selected or the dialog cancelled.
Set canCreateDirectories & canChooseFiles to false, canChooseDirectories to true
Once the user has confirmed the folder access save a security scoped bookmark in your app's prefs. Your app can now regain access to that folder at any time. With that permission you can create and open files and folders within that folder without using NSOpenPanel or NSSavePanel again.
From this point to restrict users to saving in that folder put up your own dialog to ask for just the filename, omitting the path part, and bypass NSSavePanel –you can impersonate the standard dialogs or design your own from scratch.
HTH

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.

Handling two document type

I'm developping a mac application that allow to transcribe/subtitles video. I must deal with two kind of document type:
SRT file (read/write)
Video file (read only)
For now SRT is the only document type of my application. I open video with an Open a video... menu item (under File), I connected to the openVideo action of the First responder.
Here is the code of my VideoControlerView called when pressing Open a video... :
#IBAction func openVideo(sender: AnyObject) {
let dlg = NSOpenPanel()
dlg.runModal()
if let url = dlg.URL {
self.playerView.player = AVPlayer(URL: url)
}
}
I'm sure it is possible to benefit from the multi document type handling of document based application but I can't figure how. Any idea?
After questionning Apple Developer support about it, DocumentType is only designed for the editing document (SRT in my case).
The way I handle my asset (video file) is the proper way to go!