+[NSData bookmarkDataWithContentsOfURL:]: unrecognized selector sent to class - objective-c

I am trying to resolve an alias file's original path using Objective-C(or maybe C++; it's an .mm file). Not being very much familiar, I am somehow missing + and - methods' usage. I am aware of them being class and instance methods respectively, but in practice, the following the code, with the indicated lines give me following warning and error(at build):
Class method '+bookmarkDataWithContentsOfURL:' not found (return type defaults to 'id')
-
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[NSData bookmarkDataWithContentsOfURL:]: unrecognized selector sent to class 0x7fff88942cb8'
with 0x7fff88942cb8 being the NSData address as per lldb.
Which files should I make the changes in, to get bookmarkDataWithContentsOfURL:error: and URLByResolvingBookmarkData to work?
void *pathclass::resolveAliasFromURL(const char *filepath) const
{
NSError *error = nil;
NSString *filepathh = [[NSString alloc] initWithUTF8String:filepath];
NSData *bookmarkk = [NSData bookmarkDataWithContentsOfURL:filepathh]; /*problematic line*/
BOOL isstale = NO;
NSURL *actual = [NSURL URLByResolvingBookmarkData:bookmarkk bookmarkDataIsStale:isstale error:error];/*another problematic line, but build fails already*/
NSString *urlString = [actual absoluteString];
NSLog(#"%#",urlString);
}
If there are any other faults, please point out.

Your call to bookmarkDataWithContentsOfURL: is wrong in a few ways:
The signature looks like this:
+ (NSData *)bookmarkDataWithContentsOfURL:(NSURL *)bookmarkFileURL error:(NSError * _Nullable *)error;
First, the first parameter is of type NSURL*, not NSString*. Next, you miss off the error parameter completely (despite defining a variable for it). Lastly, the method is a class method on NSURL not NSData (NSData* is the return type).
So, first, make your file path into an NSURL*:
NSURL* bookmarkUrl = [NSURL URLWithString:filepathh];
Then, call the function using the proper arguments:
NSData *bookmarkk = [NSURL bookmarkDataWithContentsOfURL:bookmarkUrl error:&error];
You should check the returned value against nil - if it's nil, then an error occurred, and the error information will be contained inside error.
The documentation is quite helpful.
Your call to URLByResolvingBookmarkData:options:relativeToURL:bookmarkDataIsStale:error: has similar problems: you are missing several parameters, the first parameter should be NSURL, etc. Again, the documentation should help.

Related

Passing NSArray of custom objects as NSData via WatchConnectivity's sendMessageData

Once a WKInterfaceController's didAppear function is fired, I send an empty NSData to WCSession's default session with the sendMessageData callback function:
// WKInterfaceController
NSData *emptyData = [[NSData alloc] init];
[[WCSession defaultSession] sendMessageData:emptyData replyHandler:^(NSData *replyMessageData) {
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:replyMessageData];
} errorHandler:^(NSError *error) {
NSLog(#"WATCH: Error from replyData %#", error);
}];
The emptyData NSData object is sent because sendMessageData: is a non-null argument. I only use it to be able to fire WCSession's Delegate method, didReceiveMessageData on the iOS app. Then the replyHandler in that very function sends the appropriate data back to the replyHandler to the WKInterfaceController.
// UITableViewController
- (void)session:(WCSession *)session didReceiveMessageData:(NSData *)messageData replyHandler:(void (^)(NSData * _Nonnull))replyHandler
{
[self loadData:nil onSuccess:^(NSArray *tips) {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:tips];
replyHandler(data);
}];
}
The problem I'm having is that I get a crash on the following line in the WKInterfaceController
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:replyMessageData];
Here's the error I get:
* Terminating app due to uncaught exception
'NSInvalidUnarchiveOperationException', reason: '*
-[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (Tip) for key (NS.objects); the class may be defined in source
code or a library that is not linked'
What I've found so far:
The NSArray I'm trying to pass is made of custom objects (Tip.m). I know that all of the objects within the NSArray must conform to the NSCoding protocol (How to convert NSArray to NSData?), which I have done properly in my opinion. I've encoded and decoded every variable and object within the object with initWithCoder and encodeWithCoder.
My Tip.m object should be added to my WatchKit Extension (NSInvalidUnarchiveOperationException cannot decode object error in Apple Watch extension). Adding the Tip.m file only gives me: "Undefined symbols for architecture i386" from other objects.
Sorry for the long post but I've tried everything to find a solution to this problem, without success. Hope this helps more people that are having issues with WatchConnectivity Framework.
I solved this temporarily by using didReceiveMessage (the NSDictionary version instead of the NSData).
I sent a manually created NSDictionary of a single NSArray that held regular NSStrings of my previous custom objects.
I have the same scenario and reached the same problem. After some searching (without any luck) and experimenting, I've solved it by adding the -all_load flag to the linker flags in the extension target.

Why does NSMutableURLRequest instantiation cause a crash?

I am trying to create a NSMutableURLRequest like this:
NSURL *URLWithString = [
NSString stringWithFormat:#"%#?%#",
urlString,
datas
];
NSMutableURLRequest* request = [[[NSMutableURLRequest alloc] initWithURL:URLWithString] autorelease];
When I run it on my iPhone 4S, the app crashes and I get the following exception:
2012-10-30 15:58:53.495 [429:907] -[__NSCFString absoluteURL]:
unrecognized selector sent to instance 0x1cd74a90
2012-10-30 15:58:53.497 [429:907] --- Terminating app due to uncaught
exception 'NSInvalidArgumentException', reason: '-[__NSCFString
absoluteURL]: unrecognized selector sent to instance 0x1cd74a90'
--- First throw call stack:
(0x361b62a3 0x344c697f 0x361b9e07 0x361b8531 0x3610ff68 0x3611363f
0x320396e7 0x32039551 0x320394ed 0x33bde661 0x33bde597 0x387e1
0x376d9f1f 0x376da9a9 0x341c535d 0x3618b173 0x3618b117 0x36189f99
0x360fcebd 0x360fcd49 0x366392eb 0x374db301 0x37cc1 0x37c58)
libc++abi.dylib: terminate called throwing an exception
What's wrong?
Lots of issues:
For a start look into how to invoke NSString and NSURL methods. I have done it for the code you have pasted.
NSURL * myUrl = [NSURL URLWithString:[NSString stringWithFormat:#"%#?%#",urlString,datas]];
and
NSMutableURLRequest* request = [[[NSMutableURLRequest alloc] initWithURL:myUrl] autorelease];
Your NSURL creation code is wrong.
NSURL *URLWithString = [NSString stringWithFormat: #"%#?%#", urlString, datas];
Here, you are trying to create an NSURL directly with an NSString class method (stringWithFormat). The result is that your variable URLWithString will be of the wrong type, and when you send it an NSURL message you'll get a crash like you are getting.
To fix this you need to create an NSString of the URL address first, and instantiate an NSURL using that, like so:
NSString *completeURLString = [NSString stringWithFormat:#"%#?%#", urlString, datas];
NSURL *completeURL = [NSURL URLWithString: completeURLString];
(Technically, these two lines can be combined; I've separated them to make it clear what's going on.)
Also, whilst probably not related to your crash, you shouldn't call your URL variable URLWithString, as that is the name for a class method of NSURL. Make sure to come up with a unique name for it, and to start it with a lower case character (at the very least, this will make your code easier to decipher for others).

How to initialize, pass argument, and check error condition using NSError**

Xcode 4.3
I've read the SO questions on NSError**, so I wrote a simple test program that uses a slightly different syntax recommended by Xcode 4.3 (see __autoreleasing below), so I'm not 100% sure if this is correct, although the code does appear to function properly. Anyway, just a simple file reader, prints an error if the file can't be found.
Questions
Would like to know if the NSError initialization, argument passing using &, and error condition checking are correct.
Also, in the readFileAndSplit.. method, I noticed a big difference between if(!*error) and if(!error), in fact, if(!error) does not work when no error condition is raised.
File Reading Method w/Possible Error Condition
-(NSArray*) readFileAndSplitLinesIntoArray:(NSError *__autoreleasing *) error {
NSString* rawFileContents =
[NSString stringWithContentsOfFile:#"props.txt"
encoding:NSUTF8StringEncoding
error:error
NSArray* fileContentsAsArray = nil;
if(!*error)
fileContentsAsArray =
[rawFileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
return fileContentsAsArray;
Caller
SimpleFileReader* reader = ...
NSError* fileError = nil;
NSArray* array = [reader readFileAndSplitLinesIntoArray: &fileError];
if(fileError){
NSLog(#"Error was : %#, with code: %li",
[fileError localizedDescription],(long)[fileError code]);
}
There are a couple of issues.
First, As per Apple's Error Handling Programming Guide, you should be checking a method's return value to determine whether a method failed or not, and not NSError. You only use NSError to get additional error information in the event that the method failed.
E.g.:
NSArray* fileContentsAsArray = nil;
NSString* rawFileContents = [NSString stringWithContentsOfFile:#"props.txt"
encoding:NSUTF8StringEncoding
error:error];
if (rawFileContents)
{
// Method succeeded
fileContentsAsArray = [rawFileContents ...];
}
return fileContentsAsArray; // may be nil
Second, NSError out parameters are typically optional and may be NULL. But if you pass a NULL error variable into your method it will crash on this line:
if (!*error) {
because you're dereferencing a NULL pointer. Instead, you must always check for NULL before referencing a pointer, like so:
if (error && *error)
{
// Do something with the error info
}
However, if you rewrite the method as indicated above then you won't be accessing the error variable at all.

ObjC / iOS: NSCFString not NSString?

I'm making a small iOS application which requests a JSON file with ASIHTTPRequest and I want it to be parsed by JSONKit. I'm using the following code:
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
// yada yada
NSURL *url = [NSURL URLWithString:#"http://localhost/file.json"]; // the file is a valid Json
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setTimeOutSeconds:20]; // Response takes too long on local machine
[request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request {
responseString = [request responseString]; // request type: ASIREQUEST, responseString is declared as NSString
NSLog(#"%# \n", [responseString class]); // NSCFString
NSDictionary *deserializedData = [responseString objectFromJSONString];
}
However, I'm seeing the following exception when the application runs:
[7646:207] -[NSCFString objectFromJSONString]: unrecognized selector sent to instance 0xdba0000
[7646:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-`[NSCFString objectFromJSONString]: unrecognized selector sent to instance 0xdba0000'`
What could be causing this exception? Why is it showing that I'm using an NSCFString here, even though I'm specifying an NSString?
NSString is something known as a class cluster. What this basically means is that when you construct an NSString you will get back something that is guaranteed to behave like an NSString as defined in its documentation, but could be implemented by some other class. In this case NSCFString is the type the system decided to return.
Anything that takes an NSString will take anything that is part of that cluster.
From looking at your code, ASI must define objectFromJSONString as part of a category on NSString. This will work perfectly fine with the clustering system, are you loading a static library that includes ASI? If so you need to add -all_load and -ObjC to your linker flags in order to get categories working from external libraries.
Edit:
Based on your comments then your solution is making sure the JSON library is included in your project. I'm assuming the objectFromJSONString was copy/pasted from some sample somewhere. It is part of the JSONKit library. Make sure both JSONKit.m and JSONKit.h are present in your project.
Update:
JSONKit is included (both .h and .m) but still the same error...
you are right, i just copied it because it seemed to do its job :)
You've included JSONKit.h, but you forgot to include JSONKit.m in the project. It compiles fine, but the implementation isn't there at runtime.
I had same problem because I enabled ARC and JSonKit doesn't support it after rectifying it worked.
Also when you include JSonKit.h and .m files please make sure you check target box too.
JSONKit declares a category called JSONKitDeserializing on NSString in order to allow you to call objectFromJSONString. Make sure you've included JSONKit.h in your source file where you are trying to use objectFromJSONString.

Converting File Path From NSString To NSURL

I'm working through Cocoa smoothly, but this problem seems so basic it cancels out all the cool stuff I learned. :/
I have a generated file path, and it needs to be in NSURL format. From research, this is the code I wrote:
NSLog(#"Old path = %#", pathToFile);
NSURL *xmlURL = [[[NSURL alloc] init] fileURLWithPath:pathToFile];
NSLog(#"New path = %#", [xmlURL absoluteString]);
And the output:
2010-01-27 15:39:22.105 MusicLibraryStats[28574:a0f] Old path = file://localhost/Users/[username]/Music/iTunes/iTunes%20Music%20Library.xml
2010-01-27 15:39:22.105 MusicLibraryStats[28574:a0f] New path = (null)
First off, the alloc-init shouldn't even be necessary; other people seem to get away with it. In this case, if I don't alloc-init, I get an 'unrecognized selector' error on that line. Of course, now I'm just getting plain old (null).
Where did I goof?
Thanks!
The [[NSURL alloc] init] is not just unnecessary, it's invalid. fileURLWithPath: is a class method, which means you can only call it on the class object (that is, NSURL itself). It does not produce a compile error because -(NSURL *)init returns an object of type id, and does not result in a runtime error because -(NSURL *)init actually returns nil, and messages sent to nil will just cascade another nil as their return value.
This code should work:
NSString* pathToFile = #"/this/is/a/path";
NSURL* url = [NSURL fileURLWithPath:pathToFile];
I found your problem.
-[NSOpenPanel URLs] returns an array of NSURL objects, which you treat as NSString objects. That's not right. You should use the following:
NSURL* url = [[oPanel URLs] objectAtIndex:0];
The debugger could've show you that if you looked at the pathToFile variable. Make sure to check it next time. :) Hovering a variable with your mouse should get you its type.
However, remember that there are situations where you will legitimately encounter another type than the one you expected. For instance, the private NSPathStore2 class is part of the NSString cluster, and you can do everything NSString supports on NSPathStore2 objects. (If this happens and you're not too sure, check the documentation to see if the type you expect is a cluster type. That's how they're called in the documentation.)