The purpose of my code is to compare the modification dates of a server file and a local file, in case that the server file is newer, it will download it.
My first attempt was to use a synchronous request using the code from http://iphoneincubator.com/blog/server-communication/how-to-download-a-file-only-if-it-has-been-updated
But it didn't worked.
After that I've been struggling to find the solution, tried asynchronous request, tried different codes I found around stackoverflow, google, etc. but nothing works.
If in terminal I do curl -I <url-to-file> I get the header values so I know is not a server problem.
This is the code I'm struggling with right now (It's written in Appdelegate.m)
- (void)downloadFileIfUpdated {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url
cachePolicy: NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval: 10];
[request setHTTPMethod:#"HEAD"];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
if(!connection) {
NSLog(#"connection failed");
} else {
NSLog(#"connection succeeded");
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self downloadFileIfUpdated]
}
#pragma mark NSURLConnection delegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSString *lastModifiedString = nil;
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
if ([response respondsToSelector:#selector(allHeaderFields)]) {
lastModifiedString = [[response allHeaderFields] objectForKey:#"Last-Modified"];
}
[Here is where the formatting-date-code and downloading would take place]
}
Right now, as it is, it gives me the error No visible #interface for 'NSURLResponse' declares de selector 'allHeaderFields'.
When I use the synchronous approach the error is that NSLog(#"%#",lastModifiedString)returns (null).
PS: If there is a better way I can explain myself or the code, please let me know.
UPDATE
The URL I'm using is of type ftp://and that may be the problem of why I don't get any HEADERS. But I can't figure out how to do it then.
Change your code to this... in the 'if' conditional, you were checking response instead of httpResponse:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSString *lastModifiedString = nil;
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
if ([httpResponse respondsToSelector:#selector(allHeaderFields)]) {
lastModifiedString = [[httpResponse allHeaderFields] objectForKey:#"Last-Modified"];
}
// [Here is where the formatting-date-code and downloading would take place]
}
... and once you feel comfortable that the response is going to always be an NSHTTPURLResponse, you could probably just get rid of the conditional statement:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
NSString *lastModifiedString = [[httpResponse allHeaderFields] objectForKey:#"Last-Modified"];
// [Here is where the formatting-date-code and downloading would take place]
}
Related
I have a little problem with my app. I want to send some http request asynchronously to server. I create this method:
- (void)sendHTTPRequest:(NSString *)urlString type:(NSString *)type idNegozio:(NSNumber *)idNegozio {
self.negozi = [[NSMutableArray alloc] init];
NSData *jsonData;
NSString *jsonString;
if ([type isEqualToString:#"shops"]) {
self.reqNeg = YES;
self.reqApp = NO;
...
jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary options:0 error:nil];
jsonString = [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];
else if ([type isEqualToString:#"appointments"])
{
[self.loadingIconApp startAnimating];
self.reqNeg = NO;
self.reqApp = YES;
...
jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary options:0 error:nil];
jsonString = [[NSString alloc]initWithData:jsonData encoding:NSUTF8StringEncoding];
NSString *requestString = [NSString stringWithFormat:urlString];
NSURL *url = [NSURL URLWithString:requestString];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:30];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody: jsonData];
NSURLConnection * conn = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
[conn start];
}
and I use this methods for connection:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
self.responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.responseData appendData:data];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse*)cachedResponse {
return nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (self.reqNeg == YES) {
//here use the responseData for my first http request
}
if (self.reqApp == YES) {
//here use the responseData for second http request
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
}
but in this way only the first connection works and I can use the responseData. While, If I try to send other http request the method connectionDidFinishLoading doesn't work and other methods too.
Anyone have an idea??
If you want to use the async request one by one you can do that:
- (void)request1 {
NSString *requestString = #"your url here";
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:[[NSURLRequest alloc]initWithURL:[NSURL URLWithString: requestString]]
queue:queue
completionHandler:
^(NSURLResponse *response, NSData *data, NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (!error && httpResponse.statusCode >= 200 && httpResponse.statusCode <300) {
// call the request2 here which is similar to request 1
// your request2 method here
}
}];
}
hope this help you~ thank you~
Your code looks good to me. Here are my ideas:
Are you sure your second NSURLConnection is being created and sent out?
Maybe it's never being sent.
Are you calling your sendHTTPRequest:type:idNegozio: method with a different type while your second connection is still sent out?
You don't have a check at the beginning of the send function to make sure you're not already sending out a connection. Maybe your flags are being switched mid-connection.
The if statements in your didFinish method should probably be combined with an else. Just in case you wanted to fire off an 'app' connection after handling a 'neg' connection you don't accidentally fall through and try to handle the response twice.
Also, you don't have to explicitly call 'start' on an NSURLConnection unless you pass NO to the startImmediately: parameter in the constructor. That shouldn't cause a problem though.
I'm looking to set up a flag on a webserver, just so that I can change something after I release my app to the app store in case a bug doesn't go away. I'm not familiar with network connections, but I've put together the following:
- (void) loadThumbnailFlag
{
NSURL *url = [NSURL URLWithString:#"http://www.myappsite.com/ThumbnailFlag"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
}
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if ([httpResponse statusCode] / 100 == 2)
{
self.thumbnailFlag = YES;
}
else
{
self.thumbnailFlag = NO;
}
}
Is there anyway to improve this code so it's not wasting any steps as just to check if the flag exists or not (i.e. it's not trying to download a file or anything).
If you don't need the body, only the response, then it's best to either set the request to type HEAD ([request setHTTPMethod:#"HEAD"]; create an instance of NSMutableURLRequest), or to call cancel on the connection in connection:didReceiveResponse:.
If the server doesn't actually return any body data then it won't make a big difference, but it makes your intentions for the connection clear.
Anybody knows how to wait the response of a http request? In my code, I am doing a http request to an url and then what i need to do, it is to check the http response in order to decide different treatment. I have something like this:
-(void)check{
[self fetchURL:#"http://something"];
if(response != nil || [response length] != 0){
do something....
}
else{
do something else....
}
}
-(void)fetchURL:(NSString *)urlWeb{
NSURL *url = [NSURL URLWithString:urlWeb];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[connection start];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
NSLog(#"INSIDE OF didReceiveResponse");
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"INSIDE OF didFailWithError");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSLog(#"INSIDE OF connectionDidFinishLoading");
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
NSLog(#"inside of didReceiveData");
response = [NSString stringWithUTF8String:[data bytes]];
NSLog(#"response: %#", response);
}
I have been trying different options that I have seen around here, but i cant stop the execution of my code and wait for that answer...that means when I check the response of my http request, it always appears empty or with a nil reference...
any help how to figure out??
thanks
You can't evaluate the response value right after your 'fetchUrl' call, because your request is asynchronous, and your code goes on with the execution without waiting for the answer. You will receive the response value only in one of the delegate method, so there's the place where you should check the result.
If you really want to make a synchronous request you can use sendSynchronousRequest:returningResponse:error: like this
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if(data){
//use data
}
else{
//check error domain and code
}
(See the Apple NSURLConnection Reference)
But keep in mind that your program will be stuck on this call till it receives a response or goes timeout.
Why don't you write this code:
if(response != nil || [response length] != 0){
do something....
}
else{
do something else....
}
In - (void)connectionDidFinishLoading:(NSURLConnection *)connection; method it wouldn't execute unless you have your complete proper response.
And Just for ado: Right to way to get data properly should be:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[_responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSString *string = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding];
if (string)
NSLog(#"string = %#", string);
}
Did you try checking the respons witin connectionDidFinishLoading: ?
That is the very delegate method which is called when the data was transferred successfully. Before that point in time, you should not expect any meaningful data.
Besides - didReceiveData should provide you with portions of data received in the meantime. Apparently you do not seem to process it nor just to store it for later evaluation (witin connectionDidFinishLoading)
I am making a login method on iOS that sends a GET request to a PHP page via url, when I try to read the output from the website, the data is read before the PHP can complete the mysql query, I was wondering if there were any way to wait until the webpage is completely done loading to read the data from it
code:
-(NSString *)getWebpageData:(NSString *)url {
NSURL *URL = [NSURL URLWithString:url];
NSError *error = nil;
NSString *content = [NSString stringWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:&error];
return content;
}
I would try using NSURLConnection via sendAsynchronousRequest like so...
NSOperationQueue *myQueue = [[NSOperationQueue alloc]init];
[NSURLConnection sendAsynchronousRequest:request queue:myQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
//do something
}];
It's mostly self-explanatory that when the handler block is fired you have your content.
Another option would be to invoke the NSURLConnectionDataDelegate. When you call your URL it will fire a few methods to let you know when things are done.
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
//Fired on error
}
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
{
//Fired First
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//Fired Second
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
//Fired Third
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//Fired Fourth
}
Using the delegate methods you're likely going to want to leverage didReceiveData so that you have your data right there. Good luck.
Our requirements include checking Internet access to a specific file on the web-server. This file is checked every n minutes. NSURLRequests never calls connection:didFailWithError whether or not there is an internet connection. And the HTTP status is always 200. Apple's reachibility only works for domains, not files- so it doesn't meet the requirements. How can I reliably discover if I can reach this file every n minutes? Why isn't the http status code really the http status code?
Other stackoverflow questions that would seem to answer this question do not work:
1. How could connectionDidFinishLoading: run if no file is found on server?
2. Testing use of NSURLConnection with HTTP response error statuses
I tried using another queue with a completion block, but that also didn't work.
-(void) updateConnectionStatus
{
NSURL *url = [NSURL URLWithString:(NSString*)[appValues getValueForSettingsKey:#"company.project.test.pingURL"]];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
//NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//__block __typeof__(self) _self = self;
connection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
/*
[NSURLConnection
sendAsynchronousRequest:urlRequest queue:queue
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
int code = [httpResponse statusCode]; // ALWAYS 200 no matter what
NSString *pingFile = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#",error); // NEVER has an error
//This doesn't even work because it remembers FOREVER the value once it gets it.
if ([#"Ping!" isEqualToString:pingFile])
{
dispatch_async(dispatch_get_main_queue(), ^{
[_self companyConnection:YES];
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[_self companyConnection:NO];
});
}
}];
*/
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(#"ERROR: %#", error); // Never get here
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse *aResponse = (NSHTTPURLResponse*)response;
NSLog(#"received a response: %ld",(long)[aResponse statusCode] );
if ([response respondsToSelector:#selector(statusCode)])
{
int statusCode = [((NSHTTPURLResponse *)response) statusCode];
// statusCode is always 200
if (statusCode >= 400)
{
[companyConnection cancel]; // stop connecting; no more delegate messages
NSDictionary *errorInfo
= [NSDictionary dictionaryWithObject:[NSString stringWithFormat:
NSLocalizedString(#"Server returned status code %d",#""),
statusCode]
forKey:NSLocalizedDescriptionKey];
}
}
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"received data");
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Finished");
}
Try with setting cachePolicy as NSURLRequestReloadIgnoringCacheData while constructing the NSURLRequest object
Thanks to Wain and Rob for putting me onto the right path. One way to keep the cache clear is adding this method to your NSURLConnectionDelegate:
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
return nil;
}