3

Im using swift and I show a NSOpenPanel. In the delegate I need to look at the sender's prompt to distinguish which action to take:

e.g.

func show() {
    ... 
    panel.delegate = self
    panel.prompt = "xy"

    panel.run ....
}

func show2() {
    ... 
    panel.delegate = self
    panel.prompt = "abc"

    panel.run ....
}

//delegate
func panel(sender: AnyObject, shouldEnableURL url: NSURL) -> Bool {
    let panelPrompt = (sender as! NSOpenPanel).prompt       ... 
}
  • without sandbox = WORKS fine

    • the sender of the delegate is a NSOpenPanel indeed
  • with sandbox = Cast fails, crash

    • the sender of the delegate is NOT a NSOpenPanel but a NSVBOpenPanel. Apple's private class that remotely speaks to the outside world and allows the user to choose files NORMALLY not in your sandbox. (for details I refer to apple's sandboxing guide)

So the question is how do I do use this in swift without crashing?
Is there a nice way or is it just a bug/ugly idk behavior
Do I have to revert to use performSelector?

===

Addition: extensions to NSOpenPanel don't work either!

Daij-Djan
  • 49,552
  • 17
  • 113
  • 135
  • What is NSVBOpenPanel? It is mentioned in the title, but occurs nowhere in the question or answer. – Martin R May 09 '16 at 15:37
  • sorry. youre right.. NSVVB stuff is apple's private sandbox open panel variant which should behave different at all AFAICS. Ill edit the question right away – Daij-Djan May 09 '16 at 15:44

2 Answers2

4

Instead of casting the sender to NSOpenPanel (which fails because the sender is an instance of the private NSVBOpenPanel class), or some performSelector magic, you can use the fact that arbitrary methods and properties can be accessed on AnyObject without casting, and the call behaves like an implicitly unwrapped optional:

func panel(sender: AnyObject, shouldEnableURL url: NSURL) -> Bool {
    let panelPrompt = sender.prompt ?? ""
    // ...
    return true
}

This gives the prompt for any sender object which has a prompt property, and the empty string as a fallback. In my test it worked well in a sandboxed environment.

See The strange behaviour of Swift's AnyObject for more details, examples, and references to the documentation.

Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • 1
    Strange, but this does work with `AnyObject`. The `sender` seems to be immutable, however. How do you mutate it? Say needing to call `sender.directoryURL = newURL` – pkamb Oct 02 '18 at 23:51
  • bug when setting `AnyObject` parameters: https://forums.swift.org/t/anyobject-parameter-cannot-assign-to-immutable-expression/16642 – pkamb Oct 03 '18 at 01:14
  • @pkamb have you found out how to change the directory of `NSVBOpenPanel`? I'm looking for a Swift version of https://stackoverflow.com/questions/5682666/restrict-access-to-certain-folders-using-nsopenpanel – LShi Jan 01 '19 at 13:50
0

This is how it would work with performSelector. It is quite ugly though:

let panelPromptUnmanaged = (sender as! NSObject).performSelector(NSSelectorFromString("prompt"))
let panelPrompt = panelPromptUnmanaged != nil ? panelPromptUnmanaged.takeRetainedValue() as! String : ""
Daij-Djan
  • 49,552
  • 17
  • 113
  • 135