Re: Safe cross references between scenes in an OS X storyboard
Re: Safe cross references between scenes in an OS X storyboard
- Subject: Re: Safe cross references between scenes in an OS X storyboard
- From: Bill Cheeseman <email@hidden>
- Date: Wed, 09 Mar 2016 14:36:23 -0500
> On Mar 9, 2016, at 1:00 PM, Quincey Morris <email@hidden> wrote:
>
> On Mar 9, 2016, at 05:59 , Bill Cheeseman <email@hidden> wrote:
>>
>> But the main thrust of my question was whether this is a safe and sensible way to crossreference other storyboard scenes -- at least when prepareForSegue(_:sender:) isn't available, as it isn't here. Having thought about it for a while now, I guess it is a sensible approach. It has less to do with storyboards than with the longstanding fact that NSApplication has a 'mainwindow' property and NSWindow has a 'contentViewController' property.
>
> I think you ended up solving the “wrong” problem. The lazy initialization is unnecessary, and so is the mucking about with optionals, because the window controller and window both exist by the time applicationDidFinishLaunching is invoked. The window isn’t main yet, but that’s the wrong place to look for it: apart from anything else, your proposed code is fragile because if your app ever has other windows that become main, even temporarily, you temporarily lose the ability to find your shoebox window.
That sort of issue is talked about in a footnote at the end of the very recent Tech Q&A QA1871, which suggests using a view controller's representedObject instead of a window for Cocoa bindings in a storyboard for the same reason. But I'm reserving representedObject for my model object, as the Q&A suggests.
It hadn't occurred to me that the issue might not be the existence of the window but only whether it had become the mainWindow by the time applicationDidFinishLaunching(_:) is called. But by using lazy initialization I no longer have to set this instance variable's value in applicationDidFinishLaunching(_:). I don't actually use the reference to the content view controller until I know it is safe (I use it in a NSMenuDelegate method when the user opens a menu in the main menu bar). But that is happenstance, not safe coding. I added 'guard', and then Optionals as you suggested, out of a sense that Swift encourages this sort of thing for safety's sake.
> (I’m talking about window controllers because, once you have a reference to the window controller, it’s easy to find the content view controller.)
>
> The storyboard is automatically instantiated in NSApplicationMain. (When the instantiation happens, there must be a strong reference to the WC stored somewhere, otherwise the WC would be deallocated, but it looks like you don’t get access to that reference in Swift projects. In Obj-C, you used to get a templated property on the app delegate.) What you can do instead is subclass NSWindowController, and use ‘windowDidLoad’ as your opportunity to save your own strong reference to the WC. (In Swift, I think I’d make it a static property of the WC class. This also lets you check that nothing creates a second main window.)
Using windowDidLoad() is a good idea. It is triggered at exactly the right time for my purposes. I already find myself setting other instance variables back up the storyboard controller hierarchy in similar circumstances. But don't I still need to use optionals if I declare the window controller or content view controller instance variable in AppDelegate but don't set it until later in the window controller's windowDidLoad() method? I am still at risk of using the instance variable before its value has been set. (I could solve that problem, or rather localize it, by not making it an instance variable but instead a local variable where I actually use it, but the whole point of my current exercise is to have a chain of storyboard scene cross references.)
You say you would make it a static property of the window controller class. But I need to refer to it in AppDelegate. So, I guess you're saying I can refer to a static class variable from anywhere. This is what the Swift language guide refers to as a "type property," right? I see from the guide that I can refer to it from any class in my application as a property of the type as opposed to a property of an instance. It strikes me that this is exactly what I need. And it feels so much more appropriate to place the reference in the window controller itself, instead of in AppDelegate. (So, a Swift "singleton" class can have a type property that refers to its own sole instance? And that type property is accessible everywhere in the application? Weird. And amazingly convenient. So much still to learn about Swift!)
> If you find this inconvenient or distasteful, your other alternative is to take the “isInitialController” status off the WC in the storyboard, and instantiate the WC yourself in (say) applicationDidFinishLaunching. Or, almost the same thing, move the window and view scenes out of the main storyboard into a separate storyboard.
It is uncomfortable, but storyboards are making lots of things uncomfortable -- or, rather, they are forcing me to do a lot of rethinking of old principles. I find myself spending a lot of time wondering which scene to use for code that in the old days used to get thrown into a single, massive window controller class where worries like this did not intrude. In this project, I am making heavy use of view controllers to break my code down into much smaller pieces, which storyboards encourage. In addition, having a lot of scenes in a single storyboard, as opposed to multiple nib files, encourages one to think that code can be put anywhere. That's why I'm inventing all the ways I can think of to create workable cross references between scenes. And Apple encourages that in the WWDC 2014 video by repeating way too many times that you really have to implement prepareForSegue(_:sender:) all over the place if you use storyboards. I guess it has always been considered more acceptable for view controllers to cross reference one another, as opposed to model classes and view classes that are supposed to be as reusable as possible.
I am aware that I can turn to loading the storyboard myself in code. My instinct is always to take advantage of anything that Apple makes automatic, until I have really exhausted all possible attempts to find a way to do that.
Thanks again for your help.
--
Bill Cheeseman - email@hidden
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden