Grabbing a web file’s last modified date

If you rely on updating files stored on a server, you want a way to find out when a file has been updated on that server, so you can download the latest version.

The easiest way I’ve found is using NSURLConnection’s sendSynchronousRequest:returningResponse:error method, but using a HEAD http method instead of GET. Here’s a simple example:

+ (NSDate*)getFileDate:(NSString*)httpFilePath {
    NSURL *url=[NSURL URLWithString:httpFilePath];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    
    [request setHTTPMethod:@"HEAD"];
    NSHTTPURLResponse *response;
    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];
    if (response==nil) return nil;
	
    NSDate *lastModifiedDate=nil;
    NSString *lastModified=[[response allHeaderFields] objectForKey:@"Last-Modified"];
    @try {
        NSDateFormatter *df = [[[NSDateFormatter alloc] init] autorelease];
        df.dateFormat = @"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
        df.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
        lastModifiedDate = [df dateFromString:lastModified];
    }
    @catch (NSException * e) {
        NSLog(@"Error formatting Last-Modified date: %@ (%@)", lastModified, [e description]);
    }
    return lastModifiedDate;
}

For completeness sake, to grab a file, we’d use code like this:

+ (NSData*)getFileData:(NSString*)httpFilePath {
    NSURL *url=[NSURL URLWithString:httpFilePath];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    
    [request setHTTPMethod:@"GET"];
    NSHTTPURLResponse *response;
    NSData *result=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];
    if (response==nil || response.statusCode>400) return nil;

    return result;
}

You can do something similar asynchronously, but its a bit more complicated.

Storing/Accessing files in your app folder for private or public use.

When you do any kind of file processing in iOS, you probably want to store the original file and/or the new file somewhere.

Public or Private?

You can store files in your apps ‘sandbox’ either for private use, where only your app can access it, or public use, where iTunes can see the files and will let you add/remove files.

For public use, we grab the NSDocumentDirectory path:

	NSArray *publicPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
	NSString *publicDocumentsPath = [publicPaths objectAtIndex:0];

(You’ll also have to add a property called ‘Application supports iTunes file sharing’, set to TRUE in your Custom iOS Target Properties section to get this working.)

For private use, we grab the NSLibraryDirectory path:

    NSArray *privatePaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
    NSString *privateDocumentsPath = [privatePaths objectAtIndex:0];

Getting a directories contents

Once you’ve got the appropriate path, its easy to iterate through the contents at that location returned by contentsOfDirectoryAtPath:error:

    NSArray *files=[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
    for (NSString *file in files) {
        NSString *filePath=[path stringByAppendingPathComponent:file];
        NSLog(@"Path for file '%@' at '%@'",file,filePath);
    }

Reading, Writing, and ArithmeticDeleting

You can see if a file/directory exists at a given path with fileExistsAtPath:isDirectory:

    BOOL isDir;
    if ([[NSFileManager defaultManager] fileExistsAtPath:filePath isDirectory:&isDir]) {
        NSLog(@"File '%@' exists as %@",filePath,isDir?@"Directory":"File");
    }

You can read a file

    NSData *data;
    if ((data=[[NSFileManager defaultManager] contentsAtPath:filePath])==nil) {
        NSLog(@"Couldn't get contents of file at '%@',filePath);
    }

You can create a directory with createDirectoryAtPath:withIntermediateDirectories:attributes:error:, which optionally creates any necessary intermediate directories for you:

    if ([[NSFileManager defaultManager] createDirectoryAtPath:folderPath
                                  withIntermediateDirectories:YES
                                                   attributes:nil error:NULL]==NO) {
        NSLog(@"Error creating folder '%@'\n",folderPath);
        return NO;
    }

You can write to a file with createFileAtPath:contents:attributes:

    NSData *data=...; // data you want to write to the file
    if ([[NSFileManager defaultManager] createFileAtPath:filePath contents:data attributes:nil]==NO) {
        NSLog(@"Error writing file.");
    }

You rename/move files and directories with moveItemAtPath:toPath:error:

    if ([[NSFileManager defaultManager] moveItemAtPath:oldFile toPath:newFile error:NULL]==NO) {
        NSLog(@"Error moving file from '%@' to '%@'\n",oldFile,newFile);
    }

You delete a file/directory with removeItemAtPath:error:

    if ([[NSFileManager defaultManager] removeItemAtPath:filePath error:NULL]==NO) {
        NSLog(@"Error deleting '%@'\n",filePath);
        return NO;
    }

Caveat

There are many other functions in the NSFileManager class that you can use.

For simplicity sake, I’ve used NULL’s for all the errors. In actual code you should handle errors appropriately.