check if file has been updated on server - ios

In my application and at startup I check if databases has been changed on server with the copy stored locally. I'm using a synchronous request to the server and I'm doing a check based on the last modified date time fields in the HTTP response
If the last modified data time of the file on the server is > last modified date time of the local file I ask user if he would like to update the database, if he accepts I download the database.
I'm using my machine as server but the problem when I shutdown my machine, the application crash at startup
Thanks for you help
You will find below my code
#import "FirstViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// check connectivity
if ([[Reachability reachabilityForInternetConnection] currentReachabilityStatus] == NotReachable) {
[self displayConenctivityAlert];
}else{
[self checkDatabases];
}
}
- (void) checkDatabases {
bool res = [self fileUpdated];
if (res){
// Ask user if he would like to update the databases
}
}
-(void) displayConenctivityAlert{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:[TSLanguageManager localizedString:#"NO_CONNECTED"] delegate:self cancelButtonTitle:[TSLanguageManager localizedString:#"OK"] otherButtonTitles:nil];
[alert show];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"Error HTTP ...");
}
- (BOOL)fileUpdated {
NSString *urlString = #"http://192.168.0.10:8888/fuel/stations.db";
NSLog(#"Downloading HTTP header from: %#", urlString);
NSURL *url = [NSURL URLWithString:urlString];
//store locally data into the resource folder.
NSString *documentsDirectory = [Utility documentsPath];
NSString *cachedPath = [documentsDirectory stringByAppendingPathComponent:#"stations.db"];
NSLog(#"Local URL / %#", cachedPath);
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL downloadFromServer = NO;
NSString *lastModifiedString = nil;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"HEAD"];
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error: NULL];
if ([response respondsToSelector:#selector(allHeaderFields)]) {
lastModifiedString = [[response allHeaderFields] objectForKey:#"Last-Modified"];
}
NSDate *lastModifiedServer = nil;
#try {
NSDateFormatter *df = [[NSDateFormatter alloc] init];
df.dateFormat = #"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
df.locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
df.timeZone = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
lastModifiedServer = [df dateFromString:lastModifiedString];
}
#catch (NSException * e) {
NSLog(#"Error parsing last modified date: %# - %#", lastModifiedString, [e description]);
}
NSLog(#"lastModifiedServer: %#", lastModifiedServer);
NSDate *lastModifiedLocal = nil;
if ([fileManager fileExistsAtPath:cachedPath]) {
NSError *error = nil;
NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:cachedPath error:&error];
if (error) {
NSLog(#"Error reading file attributes for: %# - %#", cachedPath, [error localizedDescription]);
}
lastModifiedLocal = [fileAttributes fileModificationDate];
NSLog(#"lastModifiedLocal : %#", lastModifiedLocal);
}
// Download file from server if we don't have a local file
if (!lastModifiedLocal) {
downloadFromServer = YES;
}
// Download file from server if the server modified timestamp is later than the local modified timestamp
if ([lastModifiedLocal laterDate:lastModifiedServer] == lastModifiedServer) {
downloadFromServer = YES;
}
return downloadFromServer;
}
#end

Your app is crashing because it is taking too long to complete didFinishLaunching the system watchdog kills your app. This is because you are making a synchronous http request in viewDidLoad of your root view controller, which must be completed before you can finish launching. You can resolve this issue multiple ways, either do your HTTP request asynchronously by calling sendAsynchronousRequest:queue:completionHandler on your NSURLConnection. The other option is to move this code out of your launching pipeline, perhaps by moving the code to viewDidAppear of your view controller. This has the side effect of performing the check on every return to the view, not just the initial load.
In short, doing a synchronous HTTP request is a big no no because your UI will hang until the request is complete. In this case it's especially bad because you're forcing your launch to be delayed until the request is complete, which is causing it to fail when the server is down.

Problem solved
I'm using now
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
}
instead of
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error: NULL];

Related

Async downloads with NSURLConnection prove unreliable

I have been using NSURLConnection to perform some async downloads. Basically I retrieve a PFFile from Parse backend and display it in a web view. I offer an option to download to device. Everything is good to go and all tasks perform as required, however, lately i've noticed if I perform downloads at a high rate of speed some of them don't show up in the Downloads VC. I don't know how to test it, but my theory is it's getting cut off because the priority is set to High, EVEN THOUGH, users can't do anything until the download is complete. You have to exit out of the current UIWebView to select another document. This isn't a consist behavior which makes it harder for me to narrow down. It only happens when I download > exit > open > & download another document right away > exit > repeat until done and there is no specific document it does it with.
THE PROBLEM : downloads perform, and I get an NSLog of file created, however, when I go to the Downloads VC the document doesn't show up in the NSFRC, but still shows as a valid file in the directory.
THE PROCESS
How the download happens,
The UIWebView loads a PDF queried from Parse.com. No problem here.
A ProgressHUD displays
Once loaded the progressHUD goes away.
If they want to download the PDF they just click on the action sheet index for downloading and it starts the download process.
Another Progress HUD displays as you can see in the NSURL didReceiveData displays. This HUD does not allow user interaction until the download is complete. So you can't exit the View Controller to select another pdf until it's done completely done downloading. So NO i am not conducting numerous downloads simultaneously, it's one download at a time.
Then the user can exit after download complete, select another UITableViewCell which loads the respective PDF and repeats the process.
Loading the PDF :
PFQuery *EGQuery = [PFQuery queryWithClassName:#"Classname"];
[EGQuery whereKey:#"KeyName" equalTo:self.title];
[EGQuery getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error) {
if ([object objectForKey:#"EGFile"]) {
PFFile *file = [object objectForKey:#"EGFile"];
self.pdfURL = [file url];
self.pdfName = [file name]; //Ends up as EG_2014_04 this is what I append to the filePath where it's stored so the filePath will be /PDFs/EG_2014_04
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.pdfURL]]];
} else {
}
Download method :
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Downloads"];
[request setPredicate:[NSPredicate predicateWithFormat:#"pubnumber = %#", self.title]];
[request setFetchLimit:1];
NSUInteger count = [context countForFetchRequest:request error:&error];
if (count == NSNotFound) {
} else if (count == 0) {
NSURL *url = [NSURL URLWithString:pdfURL]; //pdfURL is PFFile url
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[connection start];
NSLog(#"Background time remaining = %.1f seconds", [UIApplication sharedApplication].backgroundTimeRemaining);
}];
NSData *pdfData = [[NSData alloc]
initWithContentsOfURL:[NSURL URLWithString:self.pdfURL]];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"PDFs"];
NSString *filePath = [path stringByAppendingPathComponent:self.pdfName];
NSFileManager *fm = [NSFileManager defaultManager];
[fm createFileAtPath:filePath contents:pdfData attributes:nil];
if ([fm createFileAtPath:filePath contents:pdfData attributes:nil])
{
dispatch_async(dispatch_get_main_queue(), ^{
NSManagedObject *newDWNLD = [NSEntityDescription insertNewObjectForEntityForName:#"Downloads" inManagedObjectContext:context];
[newDWNLD setValue:self.title forKey:#"pubnumber"];
[newDWNLD setValue:self.pubTitle forKey:#"pubtitle"];
[newDWNLD setValue:self.pdfName forKey:#"puburl"]; // this is what I use for the file path name in the PDF directory and this is how I call it in my NSFRC
});
NSLog(#"File was created");
} else {
NSLog(#"File not created");
}
});
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
expectedLength = MAX([response expectedContentLength], 1);
currentLength = 0;
HUD.dimBackground = YES;
HUD.mode = MBProgressHUDModeAnnularDeterminate;
HUD.labelText = #"Downloading...";
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
currentLength += [data length];
HUD.progress = currentLength / (float)expectedLength;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
HUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"success.png"]];
HUD.mode = MBProgressHUDModeCustomView;
HUD.labelText = #"Success!";
HUD.detailsLabelText = #"Added to Downloads";
HUD.dimBackground = YES;
[HUD hide:YES afterDelay:1.6];
//Cancel Background task if completed
//[[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
//self.backgroundTask = UIBackgroundTaskInvalid;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Error %#", error);
[HUD hide:YES];
}
DOWNLOADS VC
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Downloads" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:50];
NSSortDescriptor *azSort = [[NSSortDescriptor alloc] initWithKey:#"pubnumber" ascending:YES];
NSArray *azSortArray = #[azSort];
[fetchRequest setSortDescriptors:azSortArray];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"pubnumber" cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return fetchedResultsController;
}
cellForRowAtIndexPath:
NSString *title = [NSString stringWithFormat:#"%#", [context valueForKey:#"pubnumber"]];
cell.textLabel.text = title;
etc etc
EDIT It seems to download all of them, and 'creates' a file, however it doesn't display all of them in the VC.
application DidFinishLaunchingWithOptions:
NSString *createPaths;
NSArray *documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
createPaths = [[documents objectAtIndex:0] stringByAppendingPathComponent:#"PDFs"];
NSLog(#"PDFs directory: %#", [[NSFileManager defaultManager] contentsOfDirectoryAtPath:createPaths error:&error]);
The above accrucately logs all the files that I downloaded, but they just aren't either getting recognized as fully downloaded from the UIWebView or creating the file path is going awry somehow during the download process so it's not displaying in the downloads view controller but the other documents do show up. It's consistently 1-2 missing from each session but like i stated before, its never the same document missing, it's whatever feels like missing is missing. Say I download 10 then close the app then re open it 9 will only be there in the Downloads VC sometimes 8, but it logs as being a valid filePath in the directory.
The problem appears to be your managed object context thread confinement, specifically you aren't confining. You get the context on the original thread, presumably the main thread, but then you capture it in the background block and access it directly.
You should take the result of saving your file and send it back to the main thread, then create and save the new managed object there.

AWSS3GetPreSignedURLRequest upload with custom headers giving 403

I've been having some problems with my AWSS3GetPreSignedURLRequest and AFNetworking's NSMutableURLRequest. I can successfully upload a file with just the Content-Type header. However, if I add the x-amz-acl and the x-amz-server-side-encryption, the upload will fail with 403 - No Permission error. What can I do? Is it an Amazon side problem, using server-side encryption or ACLs isn't allowed with pre-signed URLs, or changing the request will work? I looked pretty deep on the AWS documentation and the iOS SDK reference, but nothing on this. By the way, I'm using AWS iOS SDK v2. Does anyone how how to do this?
NSString *keyName;
NSString *fileContentTypeStr;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MM.d.h.mm.ss"];
if([multipleFileType isEqualToString:#"PNG"])
{
fileContentTypeStr = #"image/png";
keyName = [NSString stringWithFormat:#"image_%#.png", [formatter stringFromDate:[NSDate date]]];
}
else if([multipleFileType isEqualToString:#"JPG"])
{
fileContentTypeStr = #"image/jpeg";
keyName = [NSString stringWithFormat:#"image_%#.jpg", [formatter stringFromDate:[NSDate date]]];
}
self.imageUploadURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:#"image"]];
[imageData writeToURL:self.imageUploadURL atomically:YES];
AWSS3GetPreSignedURLRequest *getPreSignedURLRequest = [AWSS3GetPreSignedURLRequest new];
getPreSignedURLRequest.bucket = [NSString stringWithFormat:#"BUCKET-NAME/%#", folderObject.objectId];
getPreSignedURLRequest.key = keyName;
getPreSignedURLRequest.HTTPMethod = AWSHTTPMethodPUT;
getPreSignedURLRequest.expires = [NSDate dateWithTimeIntervalSinceNow:3600];
getPreSignedURLRequest.contentType = fileContentTypeStr;
[[[AWSS3PreSignedURLBuilder defaultS3PreSignedURLBuilder] getPreSignedURL:getPreSignedURLRequest]
continueWithBlock:^id(BFTask *task) {
if (task.error) {
NSLog(#"Error: %#",task.error);
} else {
NSURL *presignedURL = task.result;
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:presignedURL];
[URLRequest setValue:fileContentTypeStr forHTTPHeaderField:#"Content-Type"];
[URLRequest setValue:#"AES-256" forHTTPHeaderField:#"x-amz-server-side-encryption"];
[URLRequest setValue:#"private" forHTTPHeaderField:#"x-amz-acl"];
URLRequest.HTTPMethod = #"PUT";
URLRequest.HTTPBody = imageData;
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSProgress *progress;
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithStreamedRequest:URLRequest progress:&progress completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if(!error){
NSLog(#"File was successfully uploaded.");
}
}];
[uploadTask resume];
}
return nil;
}];
Note: The code sample there lacks a few things that are on the global headers, and a few other completion blocks + delegates, but I believe everyone will understand the point here.
Anticipated thanks,
As #YosukeMatsuda noticed in comments you can use putObjectAcl: method.
I put ACL for uploaded object on URLSession didCompleteWithError: delegate method like this:
//
self.awss3 = [[AWSS3 alloc] initWithConfiguration:__your_config__];
//
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
if (error)
{
NSLog(#"S3 UploadTask: %# completed with error: %#", task, [error localizedDescription]);
}
else
{
// AWSS3GetPreSignedURLRequest does not contain ACL property, so it has to be set after file was uploaded to bucket
AWSS3PutObjectAclRequest *aclRequest = [AWSS3PutObjectAclRequest new];
aclRequest.bucket = #"your_bucket";
aclRequest.key = #"your_key";
aclRequest.ACL = AWSS3ObjectCannedACLPublicRead;
[[self.awss3 putObjectAcl:aclRequest] continueWithBlock:^id(BFTask *bftask) {
if (bftask.error)
{
NSLog(#"Error putObjectAcl: %#", [bftask.error localizedDescription]);
}
else
{
AWSEndpoint *endpoint = self.awss3.configuration.endpoint;
NSURL *publicReadURL = [[endpoint.URL URLByAppendingPathComponent:backgroundUploadTask.bucket] URLByAppendingPathComponent:backgroundUploadTask.key];
}
return nil;
}];
}
}
We added - setValue:forRequestParameter: to AWSS3GetPreSignedURLRequest. You can use this method to add ACL and encryption headers.
Also, we introduced a new AWSS3TransferUtility for simplifying the background transfer. Refer to our blog post for more details.

NSString *latestVersionString

I am currently using this in my Xcode 5 application and I can't seem to get it working
NSString *bundleVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleShortVersionString"];
NSString *latestVersionString = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://mywebsite.com/version.txt"] encoding:NSUTF8StringEncoding error:nil];
#import NSString+compareToVersion.h
if ([bundleVersion isOlderThanVersion:latestVersionString]) {
// show a blocking modal view, with a button to link to the app store}
First, I do not recommend using synchronous network calls, regardless of how small the .txt file may be. You can try this for your code: Make sure to include <UIAlertViewDelegate> in your .h file first.
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[UIApplication sharedApplication]beginIgnoringInteractionEvents];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:
[NSURL URLWithString:#"http://mywebsite.com/version.txt"]];
request.timeoutInterval = 10.0;
request.cachePolicy = NSURLRequestReloadIgnoringLocalAndRemoteCacheData;
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:
^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
if (data)
{
NSString *bundleVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleShortVersionString"];
NSString *latestVersionString = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
if (![bundleVersion isEqualToString:latestVersionString])
{
// The two strings are not equal, dispatch to the UI thread and show a UIAlertView
dispatch_async(dispatch_get_main_queue(), ^
{
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:#"Update App!"
message:#"Please download the newer version available first on the App Store!"
delegate:self cancelButtonTitle:#"App Store"
otherButtonTitles:nil];
[alertView show];
[[UIApplication sharedApplication]endIgnoringInteractionEvents];
});
}
else
{
// Up to date; no new version is available, proceed to game play routines
[[UIApplication sharedApplication]endIgnoringInteractionEvents];
}
}
else
{
// No internet, or there is no data at the url, show error
// Uncomment below line if you want the user to interact with app again
// [[UIApplication sharedApplication]endIgnoringInteractionEvents];
}
}];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:#"Your App Store app link here"]];
}
First, check the value of latestVersionString in the debugger. Make sure it is the value you want it to be (and not e.g. nil). Also, declare an NSError * to capture any error that arises from trying to initialise a string from an HTTP URL (consider the case where there may be no internet connection, or temporary routing or DNS issue etc.).
NSError *error = nil;
NSString *latestVersionString = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://mywebsite.com/version.txt"] encoding:NSUTF8StringEncoding error:&error];
if (error != nil)
{
// figure out what went wrong based on the error
}
else
{
// check latestVersionString contains something valid and then compare
// with bundleVersion.
}
If both bundleVersion and latestVersionString contain what you expect, then the issue may be in the isOlderThanVersion: method.

Display UIAlertView if my JSON does not load?

Here is my question. How do I display an error if my app is unable to load my remote JSON file? I turned my wifi off on my computer and ran the app in the Simulator. It NSLogs the message that should be displayed if there is connection. How can I fix this? Thanks.
Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *jsonStr = #"http://xxx.com/server.php?json=1";
NSURL *jsonURL = [NSURL URLWithString:jsonStr];
// NSData *jsonData = [NSData dataWithContentsOfURL:jsonURL];
// NSError *jsonError = nil;
NSURLRequest *jsonLoaded = [NSURLRequest requestWithURL:jsonURL];
if(!jsonLoaded) {
UIAlertView *alertView = [[UIAlertView alloc] init];
alertView.title = #"Error!";
alertView.message = #"A server with the specified hostname could not be found.\n\nPlease check your internet connection.";
[alertView addButtonWithTitle:#"Ok"];
[alertView show];
[alertView release];
NSLog(#"No connection, JSON not loaded...");
}
else {
NSLog(#"JSON loaded and ready to process...");
}
}
Your code just creates the request. It doesn't actually fetch the data. You need to use NSURLConnection to fetch the data.
There are multiple ways to fetch the data. This example is for iOS 5.0 or higher:
NSOperationQueue *q = [[[NSOperationQueue alloc] init] autorelease];
NSURLRequest *jsonRequest = [NSURLRequest requestWithURL:jsonURL];
[NSURLConnection sendAsynchronousRequest:jsonRequest
queue:q
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// data was received
if (data)
{
NSLog(#"JSON loaded and ready to process...");
// ... process the data
}
// No data received
else
{
NSLog(#"No connection, JSON not loaded...");
// ... display your alert view
}
}];
You have not actually requested anything yet. Read here on how to make requests.
Basically, what you want to do is to implement NSURLConnectionDelegate protocol and override connection:didFailWithError: function to listen for failure event.

How to download file using asynchronous request?

Now, when my app detect that file was updated on server, it download file and user interface stuck for downloading time. I have ASIHTTPRequest wrapper in my app, but I doesn't know how to change my download request to asynchronous.
My code:
- (void)downloadFileIfUpdated
{
NSString *urlString = #"http://www.mysite.com/data.plist";
NSLog(#"Downloading HTTP header from: %#", urlString);
NSURL *url = [NSURL URLWithString:urlString];
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachedPath = [[documentPaths lastObject] stringByAppendingPathComponent:#"data.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL downloadFromServer = NO;
NSString *lastModifiedString = nil;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"HEAD"];
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error: NULL];
if ([response respondsToSelector:#selector(allHeaderFields)]) {
lastModifiedString = [[response allHeaderFields] objectForKey:#"Last-Modified"];
}
NSDate *lastModifiedServer = nil;
#try {
NSDateFormatter *df = [[NSDateFormatter alloc] init];
df.dateFormat = #"EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'";
df.locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
df.timeZone = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
lastModifiedServer = [df dateFromString:lastModifiedString];
}
#catch (NSException * e) {
NSLog(#"Error parsing last modified date: %# - %#", lastModifiedString, [e description]);
}
NSLog(#"lastModifiedServer: %#", lastModifiedServer);
NSDate *lastModifiedLocal = nil;
if ([fileManager fileExistsAtPath:cachedPath]) {
NSError *error = nil;
NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:cachedPath error:&error];
if (error) {
NSLog(#"Error reading file attributes for: %# - %#", cachedPath, [error localizedDescription]);
}
lastModifiedLocal = [fileAttributes fileModificationDate];
NSLog(#"lastModifiedLocal : %#", lastModifiedLocal);
[activityIndicator stopAnimating];
}
// Download file from server if we don't have a local file
if (!lastModifiedLocal) {
downloadFromServer = YES;
}
// Download file from server if the server modified timestamp is later than the local modified timestamp
if ([lastModifiedLocal laterDate:lastModifiedServer] == lastModifiedServer) {
[activityIndicator startAnimating];
downloadFromServer = YES;
}
if (downloadFromServer) {
NSLog(#"Downloading new file from server");
NSData *data = [NSData dataWithContentsOfURL:url];
if (data) {
// Save the data
if ([data writeToFile:cachedPath atomically:YES]) {
NSLog(#"Downloaded file saved to: %#", cachedPath);
}
// Set the file modification date to the timestamp from the server
if (lastModifiedServer) {
NSDictionary *fileAttributes = [NSDictionary dictionaryWithObject:lastModifiedServer forKey:NSFileModificationDate];
NSError *error = nil;
if ([fileManager setAttributes:fileAttributes ofItemAtPath:cachedPath error:&error]) {
NSLog(#"File modification date updated");
[NSThread detachNewThreadSelector:#selector(loadPList) toTarget:self withObject:nil];
[activityIndicator stopAnimating];
}
if (error) {
NSLog(#"Error setting file attributes for: %# - %#", cachedPath, [error localizedDescription]);
}
}
}
}
}
NSURL *url = [NSURL URLWithString:#"http://allseeing-i.com"];
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setCompletionBlock:^{
// Use when fetching text data
NSString *responseString = [request responseString];
// Use when fetching binary data
NSData *responseData = [request responseData];
}];
[request setFailedBlock:^{
NSError *error = [request error];
}];
[request startAsynchronous];
For more details look at http://allseeing-i.com/ASIHTTPRequest/How-to-use#using_blocks

Resources