dataWithContentsOfURL return value not nil when URL empty - objective-c

i have a problem using dataWithContentsOfURL.
I'm loading some images from the internet in a loop.
The Problem is: if the image at the URL doesn't exist, dataWithContentsOfURL does NOT return nil as expected. The apple reference says it returns nil if NSData could not be created.
Here's the Code:
NSString *TermineImgFileName = nil;
NSString *TermineImgPath = nil;
NSURL *TermineImgURL = nil;
NSData *TermineImg = nil;
for (deviceTermineHighInt; deviceTermineHighInt <= serverTermineHighInt; deviceTermineHighInt++) {
TermineImgFileName = [NSString stringWithFormat:#"Termine%i.png", deviceTermineHighInt];
TermineImgURL = [rootURL URLByAppendingPathComponent:TermineImgFileName];
TermineImg = [NSData dataWithContentsOfURL:TermineImgURL];
if (TermineImg != nil) {
TermineImgPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:TermineImgFileName];
[TermineImg writeToFile:TermineImgPath atomically:YES];
updateCount += 1;
NSLog(#"File %# saved", TermineImgFileName);
}
else {
NSLog(#"Write Error");
}
TermineImg = nil;
}
Do you know why the method doesn't return nil if the file at the URL doesn't exist?
And a second question: Does it make sense to use the Strings, NSURL and NSData as i did? I thought for memory reasons it would be the best way.
Thank you in advance,
Nikos
Edit: The variables for the loop are defined before the code, the loop works fine. Also the variable rootURL is a constant defined in the header. The URL is built fine and it works.

I'd say the question is what the server actually does if you request a non-existing image.
If it gives you a 404, -dataWithContentsOfURL: should return nil - but if it doesn't, a NSData instance with the results will be created which just happens to not contain any useful image data.

Testing the code (on a single run ). If the image does not exist I get the Write Error as expected. Whats returned is (NULL). Which I think acts the same in this case, and points to a zero value.
There is a stackoverflow post which tries to explain it Here. are-null-and-nil-equivalent

Related

Objective-C error handling

anyone know how I can handle error code when there is an error on the following:
database_flag = [NSString stringWithContentsOfURL:database_flag_query encoding:NSASCIIStringEncoding error:&error];
TO explain more please find below my code
Basically I want to check mysql for a flag
if the flag is 1 then i get the ip address of the stream from the databse
else i use the local one store.
the only issue is when there is not access to the mysql server the program gets stuck!!
database_flag_query = [NSURL URLWithString:#"http://192.168.0.20/iqkradio_stream_ip_flag.php"];
database_flag = [NSString stringWithContentsOfURL:database_flag_query encoding:NSASCIIStringEncoding error:&error];
database_flag = [database_flag stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([database_flag isEqualToString: #"1"])
{
NSLog(#"URL flag is set");
database_url_query = [NSURL URLWithString:#"http://192.168.0.20/iqkradio_stream_ip_url.php"];
database_url = [NSString stringWithContentsOfURL:database_url_query encoding:NSASCIIStringEncoding error:&error];
database_url = [database_url stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
url1 = [NSURL URLWithString:[database_url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(database_url);
}
else
{
NSLog(#"URL flag is not set, Reverting to stored value");
url1 = [NSURL URLWithString: [NSString stringWithFormat: #"http://radio.qkradio.com.au:8382/listen.mp3"]];
}
streamer = [[AudioStreamer alloc] initWithURL:url1];
EDIT - NSURLConnection & Timeouts - Based on the additional information and the comment stream below, and to put information in the answer (rather than the long comment stream):
see accepted answer to this question here for the timeout example. For the NSURLConnection example, checkout the apple documentation here
General Error Handling -
The following link may be helpful --> http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/ErrorHandling/ErrorHandling.html.
"Before you call this method, you’ll need to create a suitable pointer so that you can pass its address:
NSError *anyError;
BOOL success = [receivedData writeToURL:someLocalFileURL
options:0
error:&anyError];
if (!success) {
NSLog(#"Write failed with error: %#", anyError);
// present error to user
}
If an error occurs, the writeToURL:... method will return NO, and update your anyError pointer to point to an error object describing the problem.
When dealing with errors passed by reference, it’s important to test the return value of the method to see whether an error occurred, as shown above. Don’t just test to see whether the error pointer was set to point to an error."
So, for your issue, try adding:
if(!database_flag)
{
//call your error handling function
[myFunction withError: error];
}
before trimming the database_flag. If your connection isn't working, then you need to handle it before continueing to your if([database_flag isEqualToString:... code.
If that doesn't solve the problem, can you give some information/log statements on where/what the error is that is halting your application?
Hope that helps.

How to check if NSURL contains NSString null?

I use the following code to get a NSString from a NSDictionary and then cast it into NSUrl:
NSURL * url = [NSURL URLWithString:[self.item objectForKey:#"website"]];
The NSDictionary self.item comes from a web server and it's correctly filled using JSON data. All the other objects inside the NSDictionary work perfectly fine.
But sometimes the web server passes a website url with the text "null" because the object has no website filled in. From debugging i learned that the NSURL object can contain a url with the text "null". But how do i prevent this, or how can i write an if statement that checks this?
I tried the following:
NSString * niks = [eventUrl absoluteString];
if(niks == #"null")
{
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Event" message:#"Event heeft geen website" delegate:nil cancelButtonTitle:#"Oke" otherButtonTitles:#"Oke", nil];
[message show];
}
else
{
[webView loadRequest:[NSURLRequest requestWithURL:eventUrl]];
NSLog(#"%#",eventUrl);
}
But this doesn't work, it always passes the url directly to the webview. Can someone set me in the right direction?
Your comparison;
if(niks == #"null")
only compares if the pointers are equal (i.e. if the two are the same string object instance). Since one is a constant and the other is created dynamically from JSON fetched from the server, it's very unlikely.
To compare the content of two strings, you should instead do;
if([niks isEqualToString:#"null"])
For the link thirsty, here is the [NSString isEqualToString:] documentation.
To do string comparison, you need to do [niks isEqualToString:#"null"], that's why the first condition is broken.
You can also use [niks RangeOfString:#"null"];
I believe you can do this:
id str = [self.item objectForKey:#"website"];
if([str isMemberOfClass[NSNull class]]) {
... its null
}
The JSON convertors all change null to a NSNull object (in my experience).
I am not 100% clear of my memory, but I encountered a situation where [NSDictionary objectForKey:] actually returned NSNull class instance instead of "null" string.
If this is the case, you can check the class of [self.item objectForKey:#"website"] by using isKindOfClass method.

NSError: Does using nil to detect Error actually turn off error reporting?

I got into the habit of coding my error handling this way:
NSError* error = nil;
NSDictionary *attribs = [[NSFileManager defaultManager] removeItemAtPath:fullPath error:&error];
if (error != nil) {
DLogErr(#"Unable to remove file: error %#, %#", error, [error userInfo]);
return;
}
But looking at the documentation It seems like I got this wrong.:
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
If an error occurs, upon return contains an NSError object that describes the problem. Pass NULL if you do not want error information.
Technically there is no difference between nil and NULL so does this mean I'm actually turning this off and will never get a error message (even if the delete in the above example did fail) ?
Is there a better way to code this ?
Thanks.
First off, the following line doesn't really make sense:
NSDictionary *attribs = [[NSFileManager defaultManager]
removeItemAtPath:fullPath error:&error];
-removeItemAtPath:error: returns a BOOL value, not a dictionary.
I think I see what you’re wondering about with the NULL value. Notice carefully though, how there are 2 *'s in the error parameter in the method signature:
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
That means a pointer to a pointer. When you pass in &error, you are passing in the address of the pointer to the NSError. (Ugh, someone else can probably help me out here, as my head still starts to swim when dealing with pointers to pointers). In other words, even though you have set error to nil, you aren't passing in error to the method, you're passing in &error.
So, here’s what the re-written method should look like:
// If you want error detection:
NSError *error = nil;
if (![[NSFileManager defaultManager] removeItemAtPath:fullPath
error:&error]) {
NSLog(#"failed to remove item at path; error == %#", error);
// no need to log userInfo separately
return;
}
// If you don't:
if (![[NSFileManager defaultManager] removeItemAtPath:fullPath
error:NULL]) {
NSLog(#"failed to remove item at path");
return;
}
Passing NULL means the following:
BOOL itemRemoved = [[NSFileManager defaultManager] removeItemAtPath:fullPath
error:NULL];
i.e., the error parameter is NULL. Internally, -removeItemAtPath:error: sees if a valid pointer was passed. If it’s NULL, it simply won’t report the error as an NSError instance — but the return value will indicate whether the method completed successfully.
Also, your test is wrong. You shouldn’t be using the error output parameter to detect if an error occurred because it might be set even if the method completes successfully. Instead, you should use the return value of the method to detect errors. If the return value (in this particular case) is NO, then use the error output parameter to get information about the error:
NSError *error = nil;
BOOL itemRemoved = [[NSFileManager defaultManager] removeItemAtPath:fullPath error:&error];
if (itemRemoved == NO) {
DLogErr(#"Unable to remove file: error %#, %#", error, [error userInfo]);
return;
}
Quoting the Error Handling Programming Guide,
Important: Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object.
Edit: As NSGod pointed out, -removeItemAtPath:error: returns BOOL, not NSDictionary *. I’ve edited my answer to reflect that as well.
No I do it the same way and it works just fine for detecting errors. You are not passing NULL to it you are passing a pointer to NULL to it which is a very different thing. Although another option you might want to add is.
if (error != nil){...
}else{
[NSApp presentError:error]
}

Objective-C: initWithContentsOfURL returning null

I'm working on an app that would display a remote html and use local images, so what I'm trying to do is download the HTML of the website and display it as a "local" page, that would have access to images in my bundle.
For some reason I can't get initWithContentsOfURL to work. I checked all manuals and examples I could find and it seems that I'm doing it correctly, but the thing just won't work, returns null all the time. The same page loaded with NSURLRequest requestWithURL works fine.
Here is the code:
- (void)awakeFromNib
{
appURL = #"http://dragontest.fantasy-fan.org";
notConnectedHTML = #"Could not connect.";
NSString *seedString = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#/seed.php", appURL]]];
NSString *HTMLdata = #"";
if (seedString = #"(null)") {
NSLog(#"Can't connect on awakeFromNib.");
HTMLdata = notConnectedHTML;
}else {
HTMLdata = [NSString stringWithFormat:#"<body style='padding:0px;margin:0px;'>%#%#</body>", seedString, #"<br><img src='images/Default.png'>"];
}
[homeView loadHTMLString:HTMLdata baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] resourcePath]]];
}
Firstly, why aren't appURL and notConnectedHTML declared as NSString *? Are they declared this way elsewhere?
Secondly, you might be better off using NSURL's -urlWithString:relativeToURL: to create the actual request URL.
Thirdly (and this is your actual problem here I suspect), to compare two C primitives, you use ==. = is the assignment operator (it makes the thing on the left equal to the thing on the right). To compare two Objective-C objects, use a comparison method, like -isEqual: or -isEqualToString: (which is specifically for NSStrings).
So instead of:
if (seedString = #"(null)")
You should use
if ([seedString isEqualToString:#"(null)"])
However I suspect the reason you're trying to compare to "(null)" is because that's what NSLog spits out when an object is equal to nil. When an object is nil, the object reference itself is equal to the nil constant, so you should use this to see if an object is nil:
if (seedString == nil)
Just for good measure, some people like to use this syntax which does exactly the same thing:
if (!seedString)

Objective-C can't use stringWithContentsOfURL data in IF statement

Hey, I'm trying to ping my server and check the version of the app - all goes well so far:
NSURL *url = [NSURL URLWithString:#"http://thetalkingcloud.com/static/ping_desktop_app.php?v=1.1"];
NSError *theNetworkError;
NSString *content = [NSString stringWithContentsOfURL:url encoding:NSASCIIStringEncoding error:&theNetworkError];
//make sure something was returned
if (!content) {
//didn't connect
//my code that goes here is all fine and works
} else {
//there was some content returned by the server
//this NSLog prints out the expected data ('success' without the quotes)
NSLog(#"server returned data: >|%#|<", content);
//PROBLEM:
//neither of these two EVER become true, even when the server DOES return success or update
//connected, check what the server returned
if (content == #"success") {
NSLog(#"all went perfect");
} else if (content == #"update") {
NSLog(#"version error");
}
}
The IF statements aren't working for some reason, can anyone help me out please?
Thanks.
You are not checking the contents of content with your current conditional statement, you are checking to see if it is a valid object/pointer and not nil, which it is (valid) since you've just declared it.
Use NSString's length method and test for 0 (or isEqualToString:)
Also see this previous question for another alternative.
Use the isEqualToString: method, not pointer equality (==).