0
votes

I have a MacOS appkit app with a LOT of different NSWindows (hundreds), and they are each created from storyboards.

Many of these NSWindows have container views with complex embedded view/view controller hierarchies.

During initialization, it's necessary to know the model object associated with any given NSWindow, so its subviews and controls can be properly initialized. Since any NSController can know its NSView, and any NSView can know its NSWindow, it would be nice for that information to stored with the NSWindow.

It would be great to set a "representedObject" for the NSWindow, but unlike NSViewController, it doesn't really have one.

Is the only real solution to create a simple custom class (derived from a small base class) for each and every NSWindow storyboard object, so NSViews & NSViewControllers down the view hierarchy can get to my model data (pointer)?

A CLARIFICATION: very few of my NSWindow objects in our hundreds of storyboards have custom classes or code derived from NSWindow. So while a Category is definitely helpful for adding an API to classes to ACCESS the model data associated with the NSWindow, it's not helpful in creating a property or instance variable and initializing it in all those NSWindow storyboards.

ULTIMATELY I PRESENT A SIMPLE BUT DISGUSTINGLY BAD SOLUTION NO ONE SHOULD COPY:

Our app does not use NSDocument, which would provide a facility for associating NSWindow objects with a document/model architecture. So our goal has been to allow each and every NSController and NSView to get access to the appropriate singular document model object required to initialize the view's controls.

I've been warned by Apple engineering gurus that I cannot depend on the order in which views and subviews are created and initialized. That makes passing data down into complex storyboard embedded subviews tricky and error-prone.

But -- with all UI on the main thread, it is not possible for a single application on MacOS to create, initialize, and display one storyboard AND have another storyboard initialization & display interrupt that process (at least not our user-invoked application storyboards). So the simple solution is...

...to have a temporarily set application-level global with the desired document model pointer. That, and a stack-based lock count to insure that the above assumptions are never violated. Terrible design. Efficient solution.

No one needs to remind me WHY this is not good. But if there's a better solution it has escaped my testing. I found that even viewDidLoad and viewWillAppear can't be trusted to have a solid pointer back to its NSWindow...

1
You could add a category on NSWindow, see hereTheNextman
@TheNextman -- I could, however "you’ll need to import the category header file in any source code file where you wish to use the additional methods." My point above is that while I have hundreds of storyboards with NSWindow objects, most of them have no custom class / code associated with their NSWindow object.SMGreenfield
@TheNextman -- WOW -- that is an AMAZING article. It's well-written and informative! (if you add an answer I'll mark it as accepted). That definitely solves the storage problem (my original question). Additional thought: in order to make ANY association during the initialization of the NSWindow, SOME code has to run in order to initialize any model object field. So deriving a base class from NSWindow and applying that base class in IB would seem to be the correct way to solve the problem.SMGreenfield

1 Answers

0
votes

Without knowing your application structure; you will need a mechanism to assign the model pointer to each individual window. This will necessitate adding some code somewhere. An NSWindow subclass does seem appropriate.

In the AppKit MVC pattern, model data usually fits between the view and the view controller. Attempting to associate the model with the window is fighting against this pattern to some extent.

That being said; the Objective C runtime does allow you to add custom properties to existing classes using categories. This is achieved using Associative References. The relevant functions are:

objc_setAssociatedObject
objc_getAssociatedObject
objc_removeAssociatedObjects

This article has a good rundown of the benefits and downsides of that approach.