Change of AppDelegate class on OSX - objective-c

I have a cocoa project on OSX. For that, I had to change the class of the Appdelegate.
I did this in two steps: First I implemented the new appdelegate class (I didn't implement the NSApplicationDelegate protocol yet) and checked if it worked and compiled. In the new class I already implemented the applicationDidFinishLaunching method. Everything was still fine!
After that I changed the protocol implementation. I removed the prorocol from the old Appdelegate and inserted it in the new one. Even after this step everything seemed to be fine! The application compiled and worked as it should. But as I was somewhat sceptical, I inserted log statements in the new and the old class.
After inserting this, I had to see, that the application still used the old appdelegate!
Obviously, there is something where the name of the old appdelegate is still known! But where is that? Or do I have to do something else to reach my goal? Does anyone know what I have to do?

There are two things that have to happen: 1) an instance of your new class has to be instantiated. 2) That instance needs to be assigned to the delegate property of the application object (instance of NSApplication or a subclass).
In a typical Mac app, both of those things are done in the MainMenu NIB. That NIB contains a freeze-dried instance of the appropriate class. If you were to build the NIB from scratch, you would drag an "Object" (blue cube) from the Object library to the NIB document. By default, that would represent an instance of NSObject. You would then select it and bring up the Identity inspector where you would change its class to your new app delegate class. Since you're not building the NIB from scratch, there's already an Object in the NIB. You can select it and change its class.
The other step, assigning it to the delegate property of the application object, is done by connecting the delegate outlet of the placeholder for the application object in the NIB to the object. Again, since you're not building the NIB from scratch, that's already done.
So, in summary, you just need to change the class of the app delegate object in the MainMenu NIB.
Update: Here's a screen shot of what you have to change:

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.

Connecting delegate classes in Objective-C

I've got two controls in my Interface Builder file, and each of those controls I've created a separate delegate class for in code (Control1Delegate and Control2Delegate). I created two "Objects" in interface builder, made them of that type, and connected the controls to them as delegates. The delegates work just fine. My problem is, I need to share information from one delegate to the other delegate, and I'm not sure how.
What is the best way to do this? Combine the two delegates into one class, or somehow access a third class that they can both read? Since I'm not actually initializing the class anywhere in my code, I'm not sure how to get a reference to the actual instance of it (if there is an actual instance of it), or even access the "main" class that the project came with.
You can add outlets from either delegate to the other delegate. There are two ways to add an outlet to an object in IB (assuming you're using Xcode/IB version 3.0 or later:
If you have not generated the code for your delegate classes yet, select the desired delegate, then open the "Object Identity" tab in the IB inspector. Add a "Class outlet" of type NSObject. You should then be able to set this new outlet to the other delegate. Of course you will have to generate the code for your delegate class and add the generated source files to your Xcode project before you can load the nib.
If you've already generated the code for the delegate class (or added an NSObject to your NIB and set its Class to an existing class in your Xcode project), add an instance variable to the delegate class:
IBOutlet id outletToOtherDelegate;
As long as your Xcode project is open (as indicated by the green bubble in the lower-left of your NIB window), IB will automatically detect the new outlet and allow you to assign it to the other delegate object in your NIB.
Cocoa automatically connects these outlets at NIB load time. Once awakeFromNib is called on instances of your delegate objects, you may assume that all the other objects in the NIB have been instantiated and all outlets have been connected. You should not assume an order on calls to awakeFromNib, however.
I think you can create outlets on each one and cross-bind them so that they each have the same data all the time. If there's one model object they need to share, that's pretty tidy. I don't actually know how to do this; I think I saw it in an iPhone tutorial one time!
I don't have my Mac in front of me currently since I'm at work, but would it be possible to bind an instance of one delegate to a member of the other delegate? This would be similar to binding an NSArrayController to a member of another controller class, for example.
However, depending on what the delegate classes are doing, if the tasks are similar I would probably just combine them into once class. That would eliminate the problem altogether.