8

I have following code:

NSDataDetector* detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:nil];
NSArray* matches = [detector matchesInString:[[imagesArray valueForKey:@"content"] objectAtIndex:indexPath.row] options:0 range:NSMakeRange(0, [[[imagesArray valueForKey:@"content"] objectAtIndex:indexPath.row] length])];

This result in the log:

<NSLinkCheckingResult: 0xa632220>{235, 75}{http://URL/wordpress/wp-content/uploads/2014/04/Digital-Board-2.png}
<NSLinkCheckingResult: 0xa64eb90>{280, 25}{http://www.w3schools.com/}

What I need is to check the links wether they contain an image. In this case the first link contain an image (PNG). The second doesn't. How can I do this?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
user3423384
  • 667
  • 2
  • 8
  • 18

8 Answers8

8

You could get the NSURLs for them and compare the extensions against a list of image extensions. Something like this perhaps:

// A list of extensions to check against 
NSArray *imageExtensions = @[@"png", @"jpg", @"gif"]; //...

// Iterate & match the URL objects from your checking results
for (NSTextCheckingResult *result in matches) {
    NSURL *url = [result URL];
    NSString *extension = [url pathExtension];
    if ([imageExtensions containsObject:extension]) {
        NSLog(@"Image URL: %@", url);
        // Do something with it
    }
}
Lveecode
  • 1,022
  • 9
  • 23
Alladinian
  • 34,483
  • 6
  • 89
  • 91
6

Based on this answer you could use HTTP HEAD request and check content type.
List of possible content types for images is here.

Code sample:

- (void)executeHeadRequest:(NSURL *)url {
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:url];
    [request setHTTPMethod:@"HEAD"];
    [NSURLConnection connectionWithRequest:request delegate:self]
}

// Delegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    NSHTTPURLResponse *response = (NSHTTPURLResponse *)response;
    NSString *contentType = [response.allHeaderFields valueForKey:@"Content-Type"];
    // Check content type here
}
Community
  • 1
  • 1
Vlad Papko
  • 13,184
  • 4
  • 41
  • 57
  • I don't know why you were downvoted, really solid answer, thanks! – XelharK Nov 11 '14 at 13:18
  • This is the better answer. Checking the extension will not always work, for example a cgi that returns an image might have .cgi extension, even though it's returning image/jpeg for example. And there's a nice followup from Rags93 to simplify. – Tony the Tech Oct 30 '15 at 02:10
  • Not all servers support the `HEAD` method. – Kimi Chiu Sep 19 '20 at 15:38
6

In Swift 3, with an extension would be:

extension String {

    public func isImage() -> Bool {
        // Add here your image formats.
        let imageFormats = ["jpg", "jpeg", "png", "gif"]

        if let ext = self.getExtension() {
            return imageFormats.contains(ext)           
        }

        return false
    }

    public func getExtension() -> String? {
       let ext = (self as NSString).pathExtension

       if ext.isEmpty {
           return nil
       }

       return ext
    }

    public func isURL() -> Bool {
       return URL(string: self) != nil
    }

}

Then in your viewController (or wherever you want) :

 let str = "string"

 // Check if the given string is an URL and an image 
 if str.isURL() && str.isImage() {
     print("image and url")
 }

Note : This method works for URLs that point at an image resource with a name and an extension, for example: http://www.yourwebapi.com/api/image.jpg , but it doesn't work for URLs like this : http://www.yourwebapi.com/api/images/12626 since, in this case, the URL string doesn't tell us nothing about the mime type.

As suggested by @Visput you should look at the Content-Type HTTP Header and check the mime-type returned. For example, image/jpeg

Domenico
  • 1,331
  • 18
  • 22
1

Similar to Visputs answer, you can get the MimeType from response.MIMEType.

Here the code:

- (void)executeHeadRequest:(NSURL *)url {
  NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
  [request setURL:url];
  [request setHTTPMethod:@"HEAD"];
  [NSURLConnection connectionWithRequest:request delegate:self]
}

// Delegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
  NSLog(@"MIME: %@", response.MIMEType);
  // Check content type here
}
Rags93
  • 1,424
  • 1
  • 14
  • 14
0

The problem, of course, with checking links without looking at the resource behind them is that it fails with dynamic web services that return an image but do not have a typical image extension on the url.

Another approach you might take is to attempt to load just the HTTP header and inspect the MIME type returned. You could do this as a background task; and by loading just the header fields, you can minimize the traffic.

Here's a synchronous version of something you would probably want to do asynchronously. Just to demonstrate the idea:

#import <Foundation/Foundation.h>

BOOL urlIsImage(NSURL *url)
{
    NSMutableURLRequest *request = [[NSURLRequest requestWithURL:url] mutableCopy];
    NSURLResponse *response = nil;
    NSError *error = nil;
    [request setValue:@"HEAD" forKey:@"HTTPMethod"];
    [NSURLConnection sendSynchronousRequest:request
                          returningResponse:&response
                                      error:&error];
    NSString *mimeType = [response MIMEType];
    NSRange range = [mimeType rangeOfString:@"image"];
    return (range.location != NSNotFound);
}

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSArray *urlStrings = @[@"http://lorempixel.com/400/200/",
                                @"http://stackoverflow.com"];
        for( NSString *urlString in urlStrings ) {
            NSURL *url = [NSURL URLWithString:urlString];
            if( urlIsImage(url) ) {
                NSLog(@"%@ loads an image",urlString);
            }
            else {
                NSLog(@"%@ does *not* load an image",urlString);
            }
        }
    }
    return 0;
}
FluffulousChimp
  • 9,157
  • 3
  • 35
  • 42
  • 2
    There is also an opposite problem. Dropbox for example has share links that end in file extension, but show a html page instead of the actual file. – Filip Radelic Apr 21 '14 at 00:18
0

Since NSURLConnection is deprecated.

Similar code to obtain contentType

- (void)executeHeadRequest:(NSURL *)url {

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"HEAD"];
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];

[[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (!error) {

        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        NSString *contentType = [httpResponse.allHeaderFields valueForKey:@"Content-Type"];
        if ([contentType contains:@"image"]) {

            NSLog(@"Url is image type");
        }
    }
    [session invalidateAndCancel];
}] resume];

}

Zeeshan Tufail
  • 486
  • 3
  • 9
0

Swift 5:

extension URL {    
    var isImage: Bool {
        let imageFormats = ["jpg", "jpeg", "png", "gif"]
        return imageFormats.contains(pathExtension)
    }
}
Nimeton
  • 109
  • 1
  • 5
0

@Vlad Papko, @Zeeshan Tufail and others who use the "HEAD" httpMethod for checking is more reliable. The extension checking method is quite bad. Most recent image urls don't contain the extension anymore. I do the same "HEAD" httpMethod checking with String extension for swift version as following:

func validateImageURL(_ completion: @escaping (_ isValid: Bool) -> Void) {
    let urlString = self
    
    if let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue),
       let url = URL(string: urlString),
       let match = detector.firstMatch(in: urlString, options: [], range: NSRange(location: 0, length: urlString.utf16.count)),
       (match.range.length == urlString.utf16.count) && (url.host != nil) {
        var request = URLRequest(url: url)
        request.httpMethod = "HEAD"
        
        let task = URLSession.shared.dataTask(with: request) { (_, response, error) in
            if error == nil,
               let httpResponse = response as? HTTPURLResponse,
               httpResponse.statusCode == 200 {
                print("\(String(describing: self))::\(#function) - [\(#line)] - httpResponse.mimeType: \(httpResponse.mimeType ?? "unknown")")
                completion(httpResponse.mimeType?.hasPrefix("image") == true)
            } else {
                completion(false)
            }
        }
        
        task.resume()
    } else {
        completion(false)
    }
}
chrisK
  • 148
  • 6