AnyObject parameter: "Cannot assign to immutable expression"


#1

I'm hitting this issue in my NSOpenSavePanelDelegate in a Sandboxed Mac app.

https://developer.apple.com/documentation/appkit/nsopensavepaneldelegate/1527117-panel

I need to get a reference to the sender as a Panel and set the directory.

 /* NSSavePanel/NSOpenPanel: Gets and sets the directoryURL shown. */
 @available(OSX 10.6, *)
 open var directoryURL: URL?

All of my as? casts fail because the Sandbox uses a private subclass for these items.

If I use AnyObject, Xcode autocomplete suggests the correct method as suggested here:

But for some reason the variable is marked as immutable, and thus I cannot actually set any properties...?

extension PanelController: NSOpenSavePanelDelegate {
    
    func panel(_ sender: Any, didChangeToDirectoryURL url: URL?) {
        
        // Sandboxed app; PowerBox open/save panels
        // sender is `NSVBOpenPanel` and/or `NSKVONotifying_NSVBOpenPanel`
        // but I cannot `as?` cast to those private classes
        
        var panel = sender as AnyObject
        
        // ERROR: "Cannot assign to immutable expression of type 'URL??'"
        panel.directoryURL = URL(fileURLWithPath: "/")!

        // ERROR: "Cannot assign to immutable expression of type 'URL?'"
        panel.directoryURL? = URL(fileURLWithPath: "/")!
    }
}

Any suggestions for getting past this error or getting a reference to the Panel?


(Jordan Rose) #2

This is a longstanding bug, but it looks like we don't have a JIRA bug for it. Inside Apple it's <rdar://problem/15233922> "Cannot assign to a property through AnyObject", and it's very similar to SR-5475 (which is about optional properties of protocols). I don't think we have a good workaround other than defining a little static inline function in Objective-C that will do the call for you without trying to check anything.

NS_SWIFT_NAME(setDirectoryURL(_:for:))
static inline void setDirectoryURLForPanel(id panel, NSURL * _Nonnull url) {
  NSSavePanel *realPanel = (NSSavePanel *)panel;
  realPanel.directoryURL = url;
}

An alternate approach in this specific case would be to file a bug against Apple that the sender here is not an instance of NSOpenPanel or at least NSSavePanel.


#3

Thank you for the quick reply. I'll try that workaround. I opened a JIRA bug here:

Cannot assign to a property through AnyObject
https://bugs.swift.org/browse/SR-8900


(Quinn “The Eskimo!”) #4

Any suggestions for … getting a reference to the Panel?

By way of workarounds, it seems like the PanelController class is intended to control a panel. In that case I’d store a reference to that panel in an instance property. That way my delegate could access it directly, rather than go through the sender parameter.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple


#5

Ok, that does work and is probably the best workaround:

if sender as AnyObject === myOpenPanel as AnyObject {
    myOpenPanel?.directoryURL = URL(fileURLWithPath: "/")
}

Now I find that setting the directoryURL does not seem to work within the Sandbox! Ugh. I guess there is then no way to limit the OpenPanel to a single directory.


(Quinn “The Eskimo!”) #6

I guess there is then no way to limit the OpenPanel to a single
directory.

Alas, I don’t know the answer to that one off the top of my head. You could ask over on DevForums to see if anyone’s done that before. If that doesn’t work out, you should open a DTS tech support incident and speak to our App Sandbox expert.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple