Check if NSURL returns 404
Asked Answered
G

4

21

I need to check whether a URL (represented by a NSURL) is available or returns 404. What is the best way to achieve that?

I would prefer a way to check this without a delegate, if possible. I need to block the program execution until I know if the URL is reachable or not.

Glaze answered 10/9, 2009 at 9:54 Comment(0)
S
39

As you may know already that general error can capture by didFailWithError method:

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"Connection failed! Error - %@ %@",
          [error localizedDescription],
          [[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
}

but for 404 "Not Found" or 500 "Internal Server Error" should able to capture inside didReceiveResponse method:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    if ([response respondsToSelector:@selector(statusCode)])
    {
        int statusCode = [((NSHTTPURLResponse *)response) statusCode];
        if (statusCode == 404)
        {
            [connection cancel];  // stop connecting; no more delegate messages
            NSLog(@"didReceiveResponse statusCode with %i", statusCode);
        }
    }
}
Solangesolano answered 10/9, 2009 at 10:14 Comment(6)
Note that there's a handy class method on NSHTTPURLResponse to return a localized description for the error code: [NSHTTPURLResponse localizedStringForStatusCode: [response statusCode]]Airway
Is it possible to have this in Swift..? Thank you very muchRoughish
I'm sure you do this in Swift version. Convert by hand would not be difficult.Solangesolano
@Jirapong: I have few doubts. Which kind of errors are captured in connection: didReceiveResponse: and which are in connection: didFailWithError: ?Homesteader
@rashmi-ranjan-mallick didFailWithError is the connection cannot connect to server. didReceiveResponse is the connection able to connect but there is status along with it as standard HTTP Status Code.Solangesolano
Ok!! So, to effectively handle all the errors, I will have to handle it in both delegate methods (connection: didReceiveResponse: and connection: didFailWithError:). Am I correct? My doubt is, in case connection: didReceiveResponse: gets called with a error HTTP status code, will it also call connection: didReceiveData: or connection: didFinishLoading: after that? How should I retrieve the error message from response object?Homesteader
K
29

I needed a solution that didn't use a delegate either, so I took pieces of code shown in other answers here and created a simple method that works well in my case (and might be what you are looking for as well):

    -(BOOL) webFileExists {

        NSString *url = @"http://www.apple.com/somefile.html";

        NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0];
        NSHTTPURLResponse* response = nil;
        NSError* error = nil;
        [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
        NSLog(@"statusCode = %d", [response statusCode]);

        if ([response statusCode] == 404)
            return NO;
        else
            return YES;
     }
Kanter answered 27/7, 2010 at 20:30 Comment(2)
That is not very useful on big files. It will download the entire file before returning a responseInsignificancy
This works great, thanks. For others looking to do this, you might consider checking for a 200 status code so you know your file is accessible since any number of errors could occur besides a 404 such as 403 Forbidden and 500 Internal Server Error. Thanks!Buckingham
T
8

I used the answer from woodmantech above, but changed it based on what I have seen on other similar questions here so that it does not download the whole file to see if it exists.

I changed NSURLRequest to NSMutableURLRequest, and added:

[request setHTTPMethod:@"HEAD"];

This seems to work fine. I am working on my first app, so no real experience yet. Thanks everyone.

 NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0];
    [request setHTTPMethod:@"HEAD"];
    NSHTTPURLResponse* response = nil;
    NSError* error = nil;
    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    NSLog(@"statusCode = %d", [response statusCode]);
Tallow answered 20/2, 2014 at 0:45 Comment(0)
G
2

You can achieve a synchronous connection by calling:

NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
NSHTTPURLResponse* response = nil;
NSError* error = nil;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

Your thread will block until the request has beeen made.

Glaze answered 10/9, 2009 at 16:48 Comment(3)
Synchronous requests block the GUI and also waste resources. Asynchronous does not and allows on top of that better error handling and canceling.Recoup
Nope, that's wrong. If nothing is at url, it won't block : NSURLConnection will download the content of the 404 webpage. But you can check the status code with [response statusCode]Easternmost
@Recoup Why do you assume a synchronous request will block the GUI? What if I've very carefully placed it in an NSOperation that won't? At which point -- to get to my question -- how exactly do they 'waste resources' compared to an asynchronous request? (For that matter, how do they allow for better error handling... unless you use the NSURlConnection protocol instead, which I think you can still do if you really want to, though it's a bit more complicated and if you've already threaded the operation, is completely counterproductive and a waste of resources itself!)Grimbald

© 2022 - 2024 — McMap. All rights reserved.