Reasons for an IBOutlet to be nil - objective-c

What are the reasons why an IBOutlet (connected) could be nil?
I have one in may application which is always nil, even if I recreate everything from scratch (declaration and control).

It could be that your nib is messed up, but I find a common reason is having two instances where you think you only have one, and the one you're using in your code is not the one you connected.

If you've also defined a loadView method that creates the view, it is possible based on how you initialize it. If you initialize it using alloc-init and the nib name is not the same as class name, then you can have a case where the outlet is nil. But Chuck's answer seems more reasonable to assume.

One reason I just got stung by: If the nib file is not included in the target resource files for some reason (like you had the targets unchecked when you added it to the project), Xcode doesn't throw an error but all the outlets from that nib are going to be null...

One possibility:
Suppose the IBOutlet container is a singleton object with a function like:
+ (singletonObject*) sharedInstance {
if(!gGlobalSingletonPointer) {
gGlobalSingletonPointer = [[singletonObject alloc] init];
}
return gGlobalSingletonPointer;
}
You create the singleton object "on demand" if it doesn't already exist.
You save a global pointer to it, as you create it, in that function.
If you also instantiate such an object in InterfaceBuilder, and connect its outlets, this object will be created without sharedInstance being called. If you subsequently call sharedInstance, a new object is created (sans IBOutlet connections).
The solution is to update the global pointer in singletonObject's init or awakeFromNib function.

Are you using a UINavigationController?
If so, open your MainWindow.xib in IB and make sure that your root controller's nib name is set correctly in the Attributes Inspector.
Why would this not be set correctly? One reason is the 'rename' refactoring doesn't update this, and then the internals won't find the nib with which to wire your UI. Or you renamed the nib yourself, and didn't update this field.

Are you doing something unusual with File's Owner? If you're not in one of the situations where the nib is loaded automatically (main nib loaded by application or nib loaded by view controller, document, or window controller), then you have to load the nib programmatically.

Related

AppDelegate instantiated last on launch

I'm very comfortable with writing iOS apps, but OS X unexpectedly seems somewhat alien.
Here's the question upfront (read on for context):
When my application launches using the .xib set in the Main Interface field under the deployment info of my apps target, why does the AppDelegate get instantiated after the ViewControllers?
Context (no pun intended):
The reason I ask is because I'm using Core Data (spare me any heckling for this decision), and typically you keep a pointer to the MOC (Managed Object Context) in AppDelegate. One of my controllers is trying to get this MOC instance variable but the AppDelegate instance isn't around yet and therefore my app doesn't present data just after launch.
The AppDelegate and the two ViewControllers are in the .xib. The VCs are hooked to views inside a split view. They're trying to use the MOC in viewDidLoad to make queries. They are accessing the AppDelegate like this:
let delegate = NSApplication.sharedApplication().delegate as AppDelegate
let moc = delegate.managedObjectContext
This will crash as the .delegate property of the sharedApplication() returns nil.
I tried making an NSWindowController from the .xib in applicationDidFinishLaunching and removing the .xib from the Main Interface field, but then applicationDidFinishLaunching doesn't get called at all.
I've ensured that all the connections in IB for from the Application and the Files Owner (NSApplcation) delegate IBOutlets to the AppDelegate have been made.
UPDATE - 31/03/15
Stephen Darlington's answer below offers a good solution for my/this case. And as I understand it's actually better to setup the MOC in the way he's suggested.
If a correct answer arrives that explains why the AppDelegate is being instantiated at some later time in the launch process, I'll mark it correct instead of Stephen's. Thanks Stephen!
The "easy" solution would be to have managedObjectContext create a MOC if one doesn't exist (i.e., change it from a property to a method). That way which ever code gets there first the stack will be available.
(I'll spare the lectures about both creating the Core Data stack in the app delegate and accessing the app delegate like that!)
Here's another option without having to subclass NSApplication:
Don't put your view controllers in the .xib that you set as the Main Interface, just have the Main Menu (menu bar), AppDelegate and Font Manager in there.
Then make your view controllers in other .xibs.
Then in the applicationDidFinishLaunching method init your view controllers from their .xib files.
I also faced this issue with setting up Parse. To get around it, I simply subclassed NSApplication and set it as the Principle class in the Info.plist. In your NSApplication subclass, override the init methods and initialise Parse or anything else you need to, there.

bind the managedObjectContext outlet for a controller using Interface Builder?

I'm trying to set up a fairly simple view that presents a table to the user. This table is connected to an Array Controller, which I want to use to retrieve records from Core Data. For some reason, I can't seem to connect the 'managedObjectContext' outlet to anything else in my app. When I created my project, there was a property generated in my app delegate which returns the MOC I need, but I can't hook it up in Interface Builder, even after prepending "IBOutlet" to the declaration. Image below showing the available connection on both ends:
http://yada.im/uploads/image/screenshot/1108/7efebc90ca7187a537da9ae003dd5f3e.png
I'm sure that I'm missing some simple step here but I can't tell what piece of glue code I'm supposed to write that will allow me to hook this up more easily. For reference, I've tried dragging a line from the controller's moc outlet to every single source I could think of, and changed the "File's Owner" class to that of my application. Stumped here!
Typically in the template provided by XCode the managedObjectContext comes along with AppDelegate.
You have to bind the managedObjectContext reference of the array controller to the managedObjectContext in AppDelegate.
For this you have to make an object of AppDelegate inside the xib i.e., if its not already present.(Drag an object placeholder from your object library and make its class as AppDelegate)
This makes AppDelegate visible for binding inside that xib.
Next step is actually binding the managedObjectContext. Select your array controller and go to bindings inspector. In the parameters section select App Delegate from the drop down and check on "Bind to".
Fill the "Model Key Path" field with self.managedObjectContext. Now you will find the connection in the connections inspector also.
UPDATE:
The process of creating a new AppDelegate object is to be done only if it is not already present in the main nib file (but the stub generated always has the AppDelegate object in the main nib file).
For a non main nib file, if we follow the above approach, a new AppDelegate object will be created which won't be the NSApplication's delegate. Even though this can be solved by connecting delegate outlet of the application object proxy provided in each nib, the AppDelegate object still won't be the same.
The result is two different managedObjectContext talking to the same store. Although this might appear to work properly when the changes are saved at each step, this is not what we want.
To get the right AppDelegate object, i.e. the one used in the main nib file:
-instead of creating a new AppDelegate object, bind the managedObjectContext of the array controller directly through the application to its delegate. In other words the object to bind to will be the application object and the key path used will be self.delegate.managedObjectContext.
The way to add objects of your entity depends on the specific logic you want to implement.
The generic and easy solution would be, binding the fields for input to the array controller like you might have done for the table and then hooking up the array controller methods to the buttons inside the sheet.
Another option is sub-classing NSArrayController and over-riding the super class methods like add: to write your code (for opening your slide sheet maybe) before calling the super class method, [super add:sender] . Don't forget to specify this sub-class of NSArrayController as the class of your array controller in the xib.

How to hookup a MainWindow.xib to an empty project

Beginning with XCode 4.2, when you create an empty project using XCode 4.2, a MainWindow.xib is no longer created and hooked up for you. I've found an article that describes how to do this and I've done it and it works, but if this process has taught me anything, it has shown me that I have no idea how main(), AppDelegate and the MainWindow.xib exist together.
http://www.trappers.tk/site/2011/06/16/mainwindow-xib/
Why is MainWindow.xib class updated to UIApplication?
Why is an object placed on the xib, and then AppDelegate class is selected for the class?
Why is the delegate outlet of the File Owner connected the AppDelegate object?
Why is the the window outlet of the AppDelegate to the Window?
why does - (BOOL) application:didFinishLaunchingWithOptions: need to be commented out.
I've researched around but, I'm still not 100% sure how everything is loaded up once the application starts, and why this is setup is needed just to have a MainWindow. I have a feeling though that I should probably get all these concepts down to continue to advance in iOS development.
Why is MainWindow.xib class updated to UIApplication?
At the lowest level, nib files are loaded with the method -[UINib instantiateWithOwner:options:]. The File's Owner in a nib file is a placeholder. In Xcode, it isn't a specific object yet. It will resolve to an actual object when the nib file is loaded. Its purpose is to relate, via outlets and actions, objects inside of the nib with the object that loaded the nib. The object passed as the "instantiateWithOwner:" parameter of that UINib method is what the File's Owner placeholder in Interface Builder will resolve to.
UIApplication loads the nib file specified in the info.plist and passes 'self' for the owner parameter when loading the nib file.
By setting the class name, you're just hinting to the tools so that they can suggest the set of actions and outlets you're allowed to establish.
Why is an object placed on the xib, and then AppDelegate class is selected for the class?
When you place the generic object in the xib and change its class to 'AppDelegate' you're telling Xcode to instantiate an instance of 'AppDelegate' when the file is loaded.
Why is the delegate outlet of the File Owner connected the AppDelegate object?
UIApplication has a 'delegate' that it delegates responsibility to and notifies when interesting events occur. When you make this connection, you're setting the delegate property of the application to be the instance that you specified above. From that point on, this instance will receive the delegate messages from UIApplication, like -application:didFinishLaunchingWithOptions:.
Why is the the window outlet of the AppDelegate to the Window?
Outlets are a way to refer to objects inside of a xib. They cause properties or instance variables to be set to refer to the object pointed to by the outlet. When you make this outlet, you're making it so that the app delegate instance you created above has a way to refer to the window that's also created when the xib is loaded.
why does - (BOOL) application:didFinishLaunchingWithOptions: need to be commented out.
It represents the code-focused way to do some of the same things that are happening in the xib, and if they were both present, they would be overwriting each other.
The object placed on the XIB file is the AppDelegate because it delegates all connections in the Interface Builder, meaning if you write a method that when a button is clicked it displays text, that method would be connected to either the App Delegate or the File's Owner, preferably the delegate. The File's Owner, since it is the UIApplication, connects to the delegate because it assigns that certain object to be the App Delegate. Sorry that I couldn't answer the rest of your questions, they didn't really make sense.
Hope this helps

How can I initialize a custom GUI class?

I'm developing an iPad app.
Now I ran into the following problem. I created a "Custom Class" for a UIScrollView. So in my nib file I have added a default UIScrollView and set the Custom Class to MultiSelectView (which is the custom class I created). Screenshot here.
So in my MultiSelectView I have added some methods that I want to use. This works!
The issue is that I'm wondering how I can initialize certain objects that I need in these methods. It seems like - (id)initWithFrame:(CGRect)frame {} is not called, and neither is - (void) viewDidLoad {}.
Thus, is there a way to initialize a custom (GUI) class?
When you unarchive a view from a .xib file, it is not sent -initWithFrame:, as you've noticed. Instead, it's sent -initWithCoder:.
So, if you've got any UIView subclass in a .xib file that needs custom initialization, you'll need to override -initWithCoder: as well as (or instead of) -initWithFrame:.
Looks like you need initWithCoder, it is called when object is loaded from NIB
Or, better, awakeFromNib. The difference is that awakeFromNib is called when all outlets are connected.

Showing an NSPanel on demand - NSPanel not showing?

Hey guys,
I thought this would be an incredibly simple thing to do, but it's proving trickier than I thought.
I want to be able to show an NSPanel that displays a circular progress bar and the name of the thing that's being processed.
So, I made an NSPanel containing those controls in IB, then I created a subclass of NSWindowController. Created outlets for the controls and linked those up.
However, when I try using this code to display the NSPanel, nothing happens:
[[[self controller] msgSubject] setValue:[msg subject]];
[[[self controller] window] setLevel:NSFloatingWindowLevel];
[[self controller] showWindow:self];
[[[self controller] window] makeKeyAndOrderFront:self];
[self controller] is a method that lazily instantiates the NSWindowController subclass. I tried adding the call to makeKeyAndOrderFront: in vain, but the panel still isn't popping up.
I tried debugging and what I found is that when -initWithWindow: is called, the NSWindow that's passed in as an argument has all zeroed out instance variables, leading me to believe there's some sort of IB linking issue going on here.
Any ideas? I'm guessing I missed something really obvious, but I can't for the life of me figure out what it is.
Please post the contents of the [self controller] method so we can begin to see exactly how you're creating and using the NSWindowController subclass. Are you creating it using the initWithWindow: method directly, or is that method (which is the designated initializer) being called indirectly from one of the other init methods? If you are calling it directly, that doesn't really make sense to me as you said you already created the window in nib file itself. If on the other hand, it's being called indirectly by -initWithWindowNibName:, then it would help to see that code.
NSWindowController's are primarily used/set up in 2 different ways. One way is to create an NSWindow programmatically, and then create the NSWindowController subclass and feed that window in as the window the controller will manage. The second, and more frequently used method, is to create a nib file that houses the window, which is what it sounds like you're trying to do. In this method, you generally use the -initWithWindowNibName: initializer. As long as you pass in the proper nib name (generally without the ".nib" part of the filename), and that nib file can be properly found at runtime, and the file's owner in this nib file is set to be the custom NSWindowController subclass, and the window outlet of this subclass is properly hooked up to your window, then you should be all set.
Might want to double-check to make sure that the nib file you want to load is actually in the app bundle. (I've occasionally forgotten to add it to the target so at runtime the nib file couldn't be located and so the -initWithWindow: method would always show a nil parameter).