I'm working on my first Cocoa application, and I'm hoping very much that
[NSWindowController loadWindow]: failed to load window nib file 'Genius Document'
means that there's something very specific I've done wrong, because if I have to go back and redo all the bindings I'll want to kill myself.
FWIW, I'm working with a document-based application that has (so far) only one XIB file and no NIB files.
I can post code/screenshots of my bindings but in case that's not necessary I didn't want to make people wade through them.
Thanks for the help.
The error you have described ultimately occurs because a call to load the nib file is failing. Make sure you've supplied the correct name for your Interface Builder file.
You can supply the correct value in a number of ways (depending on your use of AppKit), so I'll lay out the two most common possibilities and you can track down which one applies to you. Given what you've said in your question, I suspect you'll be dealing with the first scenario.
NSDocument windowNibName
If you are relying on the document architecture's defaults, you are probably not making the call in question directly. Instead, the framework makes the call on your behalf, using whatever nib name you specify on the given document class.
For example, if you were to make a new document-based project with a document class of "XYZDocument," the Xcode template would provide you with a basic XYZDocument class and a XYZDocument.xib file. The XYZDocument implementation file would have the following in it:
// XYZDocument.m
- (NSString *)windowNibName {
return #"XYZDocument"; // this name tells AppKit which nib file to use
}
If you were to alter this value, you would create the [NSWindowController loadWindow] error.
NSWindowController initialization methods
If you are making this call yourself (perhaps on your own subclass of NSWindowController), then you will have written a line like the following.
// XYZWindowController.m (a subclass of NSWindowController)
- (id)init {
self = [super initWithWindowNibName:#"XYZDocument"];
if (self) {
// initializations
}
return self;
}
If the string argument you've supplied does not match the name of the nib file, the same error will occur.
I ran a Clean (Cmd-Shift-K) in Xcode and that solved the issue for me.
Related
So I am really new to cocoa programming. In fact I am very new to the Mac platform too. Still trying to get used to the fact that control+left arrow takes me to the beginning of the line.
Ok:
So I am working through the tutorials in the book 'Cocoa Programming (4th edition) by Hillegass). So I got to chapter 9, which walks through creating a document view app, that uses a NSArrayControler to bind to a NSMutableArray of Person's.
The tutorial walked me through creating a sub-class of document, and adding a NSMutableArray pointer. So I took some liberty and named it mEmployee's instead of just employees.
#interface RMDocument : NSDocument
{
NSMutableArray* mEmployees;
}
-(void) setmEmployees:(NSMutableArray*)a;
-(void) insertObject:(Person*)p inEmployeesAtIndex:(NSUInteger)index;
-(void) removeObjectFromEmployeesAtIndex:(NSUInteger)index;
-(void) startObservingPerson:(Person*) person;
-(void) stopObservingPerson:(Person*) person;
#end
Now when I did this, it seems the binding broke on the NSArrayController. So methods like setEmployee, insertObject and removeObject were never called.
Now I am still very new to objective-C, but I thought that mEmployee's was an internal member variable to my 'RMDocument' interface and that I could name it what-ever I want. I wanted to prefix the name with 'm' in order to distinguish it from other variable names (Kind of like member variables in C++). Apparently that was a big no no.
So why is the variable name had such a big effect?
I have placed the entire source for the project at:
https://www.dropbox.com/sh/fq166ap3xzlw5xc/EZJXqIZPRY/RaiseMan
Thanks!
The name of your accessor method needs to follow the naming conventions: for a property "foo", the setter is "setFoo" (note the capitalization). So, you need to have setMEmployees, not setmEmployees.
As a side note, your idea of prefixing member variables with "m" is not typical Cocoa style; it may make your code more difficult for others to read.
I have a code_tab.m file which deals with the UI and code.m file which does the crunching of data and also updates a MutableArray. When the code.m updates the array I want to update the table in view linked to the connection_tab.m. I have a function in code_tab.m that has a:
[self.table tableupdate]
Which works as expected when called internally. When the same function is called from the code.m file (when the array has been updated) nothing happens even though I can see the same code is being accessed.
I have setup access by having this line in the code.h file:
connection_tab* connectiontab_script;
and then calling it in code.m with
connectiontab_script = [[connection_tab alloc] init];
[connectiontab_script tableupdate];
I've obviously missed something vital. Is there a way that one method can alter the visual display of another, in this case update the table already on screen?
Technically you should avoid having your data class make calls to methods in your view as it will likely cause you headaches in future, MVC is the recommended development practice.
However, you could achieve what you're trying to do by amending your initNetworkCommunication method in code to pass a reference to the active view through:
- (void) initNetworkCommunicationForView:(UIViewController*)passedVC
Which you'd call from inside connection_tab like:
[appDelegate.connection_script initNetworkCommunicationForView:self];
Then in code you'd do (if statement avoids warnings):
if ([passedVC respondsToSelector:#selector(tableupdate)]) {
[passedVC performSelector:#selector(tableupdate)];
}
Depending on the structure of your code class you may need to create a class-wide variable to store passedVC in if the tableupdate call isn't within initNetworkCommunication.
P.S You could also customize the method to be specific to your connection_tab class like:
- (void) initNetworkCommunicationForView:(connection_tab*)passedCT
Then you could ditch the selector stuff and go with:
[passedCT tableupdate];
But I went with UIViewController for compatibility reasons.
In Aaron Hillegass' Cocoa Programming for Mac OS X, the Raiseman application connects a button in Interface Builder (IB) to an NSArrayController with sent action -remove:. In the MyDocument class he implements two KVC methods:
- (void)insertObject:(Person *)p inEmployeesAtIndex:(int)index;
- (void)removeObjectFromEmployeesAtIndex:(int)index;
When this button is pressed, the -removeObjectFromEmployeesAtIndex: method is called and the currently selected Person (Model) object is removed from the array.
How does the remove: method used in IB cause the -removeObjectFromEmployeesAtIndex: method to be called?
How do I reproduce this effect with an NSTreeController?
If you want a simple built-in option, then it's only going to create an instance of the class you specified in IB. To create another instance, you're going to need to code it yourself. You should have all the information you need from the Tree Controller to insert the new class into the proper place in the hierarchy. Some diligent searching should give you the code you need.
To attempt to help you understand how the NSArrayController mechanism works, I'll explain the best I can from my knowledge of Objective-C and the runtime. Objective-C is a very dynamic language, and you can dynamically call selectors (methods). Since the NSArrayController knows the name of your class (e.g. "Employee"), its internal implementation probably looks something like the following (or easily could):
NSString *removeSelectorName = [NSString stringWithFormat:#"removeObjectFrom%#sAtIndex:",
self.objectClassName];
SEL removeSelector = NSSelectorFromString(removeSelectorName);
[dataRepresentation performSelector:removeSelector
withObject:[NSNumber numberWithInt:self.selectionIndex];
There are examples of this elsewhere in KVO, as with the +keyPathsForValuesAffecting<Key> method (documentation here), which describes which keys cause another key to be updated. If your key is named fullName and it updates whenever the first or last name changes, you would implement this in your class:
+ (NSSet *)keyPathsForValuesAffectingFullName {
return [NSSet setWithObjects:
#"firstName",
#"lastName",
nil];
}
Further searching (and this question) turned up this documentation page, which explains the semantics of how that method gets called.
I need to be able to compare two versions of a plist file created with NSKeyedArchiver. In particular, it's the "elements" file created in Xcode for a .xcdatamodeld file.
Since I have not created this file myself I can not recreate its object model. Instead, I need to understand which classes and keyed properties the archive contains.
Ideally, I want to create a tree containing strings representing the names of classes along with their property names and values. I assume that all this information is stored in the archive, so it should be possible to generically parse this archive, right?
I've read about NSKeyedUnarchiver and its delegate. I've only gotten as as as this:
Unarchive the file data:
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
unarchiver.delegate = self;
id graph = [unarchiver decodeObjectForKey:#"root"];
Here's the delegate method that gets called when an unknown class is requested. I return a proxy class here in which I hope to collect its values:
- (Class)unarchiver:(NSKeyedUnarchiver *)unarchiver cannotDecodeObjectOfClassName:(NSString *)name originalClasses:(NSArray *)classNames
{
NSLog(#"wants: %#", name);
return [ObjProxy class];
}
The proxy class implements
- (id)initWithCoder:(NSCoder *)aDecoder
in which I do not know how to proceed, not knowing the actual properties of the classes. NSCoder doesn't seem to provide any function to learn of the available keys. Is there a trick to get to them, maybe by overriding some of the lower level objc methods?
So far, with this little code shown above, when parsing the "elements" file, I only get the request for one class, "XDPMModel", and then it's done.
Any ideas how to get this working, i.e. traverse the tree deeper?
You can use PlistExplorer, it's a Cocoa Tool to inspect files written by NSKeyedArchiver.
I've done exactly this before, to decode objects stored in the preferences file for Panic's Coda. What I did was to use class-dump on the app (in your case Xcode and its related frameworks), which allows you to see the properties of the real objects, and use these to create proxy objects that match those properties. You can then use NSKeyedUnarchiver successfully and query the proxy objects for their values.
It's a fairly laborious process but it works perfectly. In my case I was working with a user defaults plist so I only had to define a couple of these proxy classes for the objects I was interested in, but I would imagine that you'll have to define quite a few for Xcode.
I'd also be very interested to know if there's a way to do this without having to go the class-dump route (possibly, as you say, via some of the lower-level Objective-C functions) because it would greatly simplify this type of reverse engineering.
I have a WebView that I need to ask to load a website from a different class.Therefore I implemented the following function:
-(void)performResearch:(NSString *)aString {
NSLog(#"Received Request!");
[[researcher mainFrame]
loadRequest:[NSURLRequest
requestWithURL:[NSURL URLWithString:aString]]];
}
In my other class, I call the function like this:
Researcher *res = [[Researcher alloc] init];
[res performResearch:#"http://www.twitter.com"];
[res release];
I get no compiler errors and the NSLog does indeed get called, however the WebView doesn't load the webpage. No errors in the Log either.I'm puzzled.
Could anyone tell me what's wrong?
-(void)performResearch:(NSString *)aString {
NSLog(#"Received Request!");
[[researcher mainFrame]
loadRequest:[NSURLRequest
requestWithURL:[NSURL URLWithString:aString]]];
}
In my other class, I call the function like this:
Researcher *res = [[Researcher alloc] init];
[res performResearch:#"http://www.twitter.com"];
[res release];
Within performResearch:, the object you sent the performResearch: message to in the latter code is self. researcher is some other variable; you are talking to a different object that is probably not this Researcher.
In your comment on Colin Gislason's answer, you say that researcher is not a Researcher, but is a WebView. You would do well to correct this inaccurate choice of variable name.
Once you've renamed the variable to something more accurate, like webView, you then need to make sure there is something in it. Simply having a variable is of no help; you need to have a pointer to an object in the variable. Otherwise, the pointer in the variable is nil, and messages to nil do nothing at all. They do not crash, they do not fail, they do not succeed, they do not break—execution continues as if nothing happened, because it did.
You say that the variable is an IBOutlet; this implies that you mean to hook up the outlet in IB. One possible cause of your problem is that you forgot to do that, which would explain the variable containing nil. Make sure it's connected in IB.
The other possible cause of your problem is that this nib has not been loaded. If the Researcher object owns the nib, then it is generally its responsibility to load it, which means you need to do that. If some other object is loading a nib with both a Researcher and a WebView in it, then your creation of another Researcher object in the latter code snippet is wrong; this second Researcher object has no relation to the one in the nib, nor has it a WebView from any source. In this latter case, you would have to cut out the creation of a Researcher object, and use the one in the nib.
That last solution may require adding an outlet to the object whose class hosts the second code snippet. I disapprove of that solution. It's difficult to say what you should do because I'm not sure where Researcher objects fit in the MVC triumvirate. I suspect you need to think some more about which category it's in; I think that it should be a Controller, and that you will need to do some refactoring to re-establish the boundaries you should have between powers.
Failure to uphold MVC in your app generally results in Cocoa being harder to use than it normally is.
You don't show where researcher is declared, but it looks like a normal instance variable. Where does the value come from?
If it comes from outside the Researcher class, then it looks like it is nil because your other class is creating a new Researcher instance but not providing the researcher attribute.. If you pass messages to nil objects, the message is ignored. That is why you don't get an error. Try breaking the load request line out into several lines and log the values of the objects.
EDIT:
Interface Builder sets the UIWebView attribute of one particular instance of the Researcher class. To update that web view, you need to call performResearch on the same instance. You cannot use a new instance because it won't be connected to a web view at all.
If possible, you should pass the original Researcher object to the new class and use that instead:
[self.researcher performResearch:#"http://www.twitter.com"];