problems with iphone code to check server file date - iphone

I want to check the dates on files -- zip, jpg, or whatever -- on my server and, if they are newer than the copies on my iPhone, to download them.
I wrote the following method based on a post here that's about a year old. It has two problems:
+ (NSString *) f_GetServerFileDate:(NSString *)MyURL {
NSURL *oURL = [NSURL URLWithString:MyURL];
NSURLRequest *oRequest = [NSURLRequest requestWithURL:oURL];
NSHTTPURLResponse *oResponse;
[NSURLConnection sendSynchronousRequest:oRequest returningResponse:&oResponse error:nil];
if ( [oResponse respondsToSelector:#selector( allHeaderFields )] ) {
NSDictionary *metaData = [oResponse allHeaderFields];
return [metaData objectForKey:#"Last-Modified"];
} else {
return #"00000000";
}
}
Problem 1: It returns "00000000" when given "http://www.mysite.com/myzip.zip" as a URL.
Problem 2: For an Active Server Page (just a test; not that I'd really download one) it returns a date that has no bearing on the date the file was last uploaded or modified.
What's the right way?

This is probably less a problem of your iPhone code but rather of the web server. A web server is not required to include the Last-Modified header attribute. And for dynamic pages (such as ASP pages) it is correct to return the date when the page was dynamically generated (i.e. the current date) and not the date when the page was developed or deployed.
I suggest you use a browser extension such as Live HTTP headers for Fireofx to observe what HTTP header attributes the web server returns. If the date is missing, you'll be out of luck (unless you have access to the web server and can fix it there).
Furthermore, your code will always download the image no matter when it has been last modified. You can prevent this if you include the HTTP header If-Modified-Since in your request. That way the web server will send the image if it has been modified since the specified date or just send a 304 (Not modified) result code if the image is still up to date. But again: It depends on the web server if this option is supported and it only works for static content unless the author of the web application has specifically implemented it.

Related

iOs application fails to open a file associated with the application (swift) [duplicate]

I've seen many questions on SO concerning converting between NSURL and NSString. They all involve using either NSString *path = [myURL absoluteString]; or NSString *path = [myURL path];. What is the actual difference between these methods? Is there a time when one should be used over the other? I tried consulting the Apple Docs, but I found it less than helpful.
I'm used to URL's only being mentioned in discussions concerning websites and other topics regarding sending information between different machines, and never being mentioned when dealing with just the file structure on a single machine. Perhaps this is where some of my confusion is coming from, since NSURL seems to be the preferred way of accessing files, regardless of whether that file exists on a network or on the local device. Or maybe that's a totally unrelated topic. I'm not even sure.
Question 1:
What is the actual difference between these methods?
Let's analyze this writing 6 lines of code - 3 for a local and 3 for http URL - and playing around with them a little bit.
Let's create an NSURL using the file:// scheme. If you ask yourself why there are 3 / after file: you should remember that a complete URL exists of a scheme (file:// and absolute or relative path (you can find more information on creating URLs in RFC 1808 on page 3). We use an absolute path which starts with a / so that we end up with ///.
NSURL *aLocalURL = [NSURL URLWithString:#"file:///Users/dennis/Desktop/"];
NSLog(#"absolute string: %#", aLocalURL.absoluteString);
NSLog(#"path: %#", aLocalURL.path);
Output:
absolute string: file:///Users/dennis/Desktop/
path: /Users/dennis/Desktop
So we see that absoluteString still knows its scheme whereas path doesn't have this information anymore.
Note: path is a file (directory) URL and as the docs state, the trailing slash it is stripped.
Now let's take a look at remote URLs. With these type of URLs most people are more familiar. We create it using the same procedure as for local URLs. Our scheme is now http:// and our path is www.apple.com/.
NSURL *anHTTPURL = [NSURL URLWithString:#"http://www.apple.com/"];
NSLog(#"absolute string: %#", anHTTPURL.absoluteString);
NSLog(#"path: %#", anHTTPURL.path);
Output:
absolute string: http://www.apple.com/
path: /
Again, we see that the absolute string still knows its scheme but path is now /. So path seems to be not an appropriate way when working with remote URLs.
However, when we have an URL like http://www.apple.com/index.html we get
absolute string: http://www.apple.com/index.html
path: /index.html
Reading the docs helps here, too:
Per RFC 3986, the leading slash after the authority (host name and port) portion is treated as part of the path.
So the path is everything beginning (and including) at the slash after the authority which is www.apple.com in our case.
Question 2
Is there a time when one should be used over the other?
From the docs: (method: path)
If this URL object contains a file URL (as determined with isFileURL), the return value of this method is suitable for input into methods of NSFileManager or NSPathUtilities.
In my opinion that sentence states clearly that you should use path when you work with NSFileManager or NSPathUtilities.
Conclusion:
When you work with remote URLs you (generally) use absoluteString, otherwise the result is not what you (generally) want.
When you work with local URLs use path.
Sources:
http://www.ietf.org/rfc/rfc1808.txt
http://www.ietf.org/rfc/rfc3986.txt
NSURL Class Reference
Adding to HAS' response -- the Apple docs mention that Path-based URLs are simpler in some ways, however file reference URLs have the advantage that the reference remains valid if the file is moved or renamed while your app is running.
From the documentation for "Accessing Files and Directories":
"Path-based URLs are easier to manipulate, easier to debug, and are generally preferred by classes such as NSFileManager. An advantage of file reference URLs is that they are less fragile than path-based URLs while your app is running. If the user moves a file in the Finder, any path-based URLs that refer to the file immediately become invalid and must be updated to the new path. However, as long as the file moved to another location on the same disk, its unique ID does not change and any file reference URLs remain valid."
https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/AccessingFilesandDirectories/AccessingFilesandDirectories.html
One further note, and I've only tried this for Swift and URL not NSURL. The relativeTo form of URL:
URL(fileURLWithPath: aPath, relativeTo: URL)
generates a URL that behaves not fully like a remote URL (as in #HAS above) and not like a file URL.
So, for example:
let url0 = URL(fileURLWithPath: "/Foo")
let url1 = URL(fileURLWithPath: "Bar", relativeTo: url0)
print("\(url1.path)")
// Output: "/Bar\n"
(similar to results for a remote URL, but not a file URL).
If we use absoluteString, we get:
print("\(url1.absoluteString)")
// Output: "file:///Bar\n"
(not similar to either a file URL or a remote URL).

Validate a XML file in IOS

I have a XML parser. I'm getting the XML file from server and write that XML file in to a local file in cache. Before do that, I want to check the URL has the XML file. How Can I check the available URL's page is a XML page or another type of page(Ex:HTML,PHP)?? Simply how can I identify a XML file ??
Ultimately, you have to look at the contents of the data retrieved to make sure it's valid XML, and parsing is the easiest way to do that.
If you're retrieving the data via a HTTP request, you can, though, also look at the response you receive before you start receiving the actual data. For example, if using NSURLConnection, you can implement a didReceiveResponse, which should often return a 200 for status code and text/xml for content type:
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if ([response isKindOfClass:[NSHTTPURLResponse class]])
{
NSHTTPURLResponse *httpResponse = (id)response;
NSInteger statusCode = httpResponse.statusCode;
NSString *contentType = httpResponse.allHeaderFields[#"Content-Type"];
NSLog(#"%d; %#", statusCode, contentType);
// check to see if statusCode == 200 and/or [contentType isEqualToString:#"text/xml"] here;
}
}
As an aside, the status code and the content type are set by the server, so it is, admittedly, dependent upon the server's implementation (e.g. if the XML is being generated programmatically by the server, hopefully it's setting these HTTP response fields correctly, but if you're retrieving XML from third party servers, you can't be guaranteed that they're well-behaved). But a status code of 200 and content type of "text/xml" are customary and most servers will set these values appropriately if you're just retrieving a XML file.
The most reliable technique for validating your XML is to just receive the data from the server, and submit it to a parser, and see if the parser returns an error or not.
There are various solution available for this:
http://knol2share.blogspot.in/2009/05/validate-xml-against-xsd-in-c.html
http://wiki.njh.eu/XML-Schema_validation_with_libxml2
Checking for proper xml before parsing in NSXMLParser
Hope this will help you.

Invalid S3 signature error when using server-side encryption and ASIHTTPRequest from an iPhone app

I have an iPhone application I've been using for some time that uses ASIHTTPRequest to upload videos to a bucket on Amazon S3. It has been functioning well without any problems. Recently, we decided to make use of the new "server-side encryption" that Amazon has implemented. This allows you to tell Amazon's server to encrypt files that have been posted to a bucket automatically by including an additional HTTP request header.
I have added a single line of code to my application to implement this, but now my Amazon uploads are failing. The specific error message that is appearing is:
"The request signature we calculated does not match the signature you provided. Check your key and signing method."
The name of the bucket I am using conforms to Amazon's naming standards, so I am confident that is not the issue. I am also confident that the secret and public keys I am using are correct.
It would appear that adding this header is somehow breaking the signature calculation, I am assuming because it is being included in the calculation on one side of the transmission but not the other.
Am I doing this incorrectly? Or is this a bug in ASIHTTPRequest?
Here is my code for reference:
[ASIS3Request setSharedSecretAccessKey:#"mysecretkey"];
[ASIS3Request setSharedAccessKey:#"myaccesskey"];
NSString *bucketPath = [NSString stringWithFormat:#"mypath/filename"];
ASIS3ObjectRequest *request = [ASIS3ObjectRequest PUTRequestForFile:filepath withBucket:#"my-bucket" key:bucketPath];
// If the following line is commented, the upload completes successfully
[request addRequestHeader:#"x-amz-server-side-encryption" value:#"AES256"];
////
request.requestScheme = ASIS3RequestSchemeHTTPS;
[request setShouldContinueWhenAppEntersBackground:YES];
[request startSynchronous];
if ([request error])
{
// The error messag is being displayed here
NSLog(#"xmit error: [%#]",[[request error] localizedDescription]);
}
You're doing everything right, the issue is that constructing the Authorization header (i.e. "the request signature") involves signing a string which includes all of the x-amz- headers; you've added one such header (x-amz-server-side-encryption), but you've not caused it to be factored into the signature.
I just created a branch of ASIHTTPRequest with support for SSE. If you use that branch, you should just be able to say [request setUseServerSideEncryption:YES];. Alternately, if you're more interested in the technique, here are the details of making it work.
I'm having the same issue, however I'm not using encryption. But what I found so far is that upper case letters get the wrong signature.
In my case I'm setting the storage class with ASIS3StorageClassReducedRedundancy that has a value of #"REDUCED_REDUNDANCY" which is in uppercase. If I don't set this option the request goes successful.
So maybe your problem is with the value AES256 that has uppercase letters.

Pause/Resume downloads in Objective-C

Alright. Hopefully this will be my last post about the download manager I am writing in Objective-C. Everything seems to work well except the pause/resume functionality. My issue is that when a download tries to continue from where it left off, it appends the data it receives to the file, but it still seems that it's trying to download the entire file. This results in a file that is larger than the original file is supposed to be. Here is the code I am using for downloading files. Am I doing something wrong?
-(void)start:(unsigned int)fromByte {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.url] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:DEFAULT_TIMEOUT];
// Define the bytes we wish to download.
NSString *range = [NSString stringWithFormat:#"bytes=%i-", fromByte];
[request setValue:range forHTTPHeaderField:#"Range"];
// Data should immediately start downloading after the connection is created.
self.urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:TRUE];
if (!self.urlConnection) {
#warning Handle error.
}
}
I see you are specifying the Range header in the request. First thing to check is whether the server is actually honoring the Range request, by checking the headers in the response object (which should be an NSHTTPURLResponse) in connection:didReceiveResponse: for a proper Content-Range.
I finally figured this out. It turns out that the 'getFilesizeInBytes' method I had was get the NSFileSize object from the file's attributes, but I was directly casting this to an int. This caused the number to be about 20 times larger than it should have been. I was able to fix this by using [#"" intValue]. Once this was fixed, the servers were able to give me the rest of the file starting with the correct byte. It seems that before my issue was not that the server wasn't honoring my request, but that it couldn't honor my request due to me requesting data that was well beyond the final byte of the file.
There's no support for pause/resume in NSURLConnection. You can emulate it by stopping the request, then issuing a request for the rest of the content with a Range header on resume. Some support from the HTTP server is required for that, and is not guaranteed.
Looks like lack of support at the server is what you're facing.
Most download managers, however, implement their own HTTP stack on top of sockets.

Creating a highscore like system, iPhone side

I'm sorry for opening a new question, I had to - as I wrote the other question from my iPhone as unregistered user and it is not very comfortable to write from the iPhone.
Rephrasing the question:
Is it possible to use the:
[NSMutableArray writeToURL:(NSString *)path atomically:(BOOL)AuxSomething];
In order to send a file (NSMutableArray) XML file to a url, and update the url to contain that file?
for example:
I have an array and I want to upload it to a specific URL and the next time the app launches I want to download that array.
NSMutableArray *arrayToWrite = [[NSMutableArray alloc] initWithObjects:#"One",#"Two",nil];
[arrayToWrite writeToURL:
[NSURL urlWithString:#"mywebsite.atwebpages.com/myArray.plist"] atomically:YES];
And at runtime:
NSMutableArray *arrayToRead =
[[NSMutableArray alloc] initWithContentsOfURL:[NSURL urlWithString:#"mywebsite.atwebpages.com/myArray.plist"]];
Meaning, I want to write an NSMutableArray to a URL, which is on a web hosting service (e.g. batcave.net), the URL receives the information and updates server sided files accordingly.
A highscore like setup, user sends his scores, the server updates it's files, other users download the highscores at runtime.
I hope this is clarified.
Edit: What I am looking for is scripting PHP or ASP so the website, the URL where the data is sent to would know how to handle it. I want an example or a tutorial on how to implement this scripting for handling data, if it's possible to do this on a web hosting service.
~Thanks in advance.
To answer the question "How do I create a high score like system?", there are multiple parts of the system:
You need an ID for each user (a GUID generated on the iPhone, together with the users name should be sufficient).
You need a server that: remembers high scores; receives high scores from users; either displays (on a web site) the high scores and/or makes the high scores available for download to the phone.
You need some fraud protection, although that is likely fighting a losing battle against jailbreakers.
On the iPhone app side, you might want to be able to download the current high scores for display, which is done easily enough with something like:
int statusCode = 0;
NSData* result = nil;
NSHTTPURLResponse* response = nil;
NSError* error = nil;
NSString* url = #"http://www.yourserver.com/highscores.php"; // returns XML plist data
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:180];
result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
// NSLog( #"NSURLConnection result %d %# %#", [response statusCode], [request description], [error description] );
statusCode = [response statusCode];
if ( (statusCode == 0) || (!result && statusCode == 200) ) {
statusCode = 500;
}
Since it is synchronous, you might want to put it inside an NSOperation. Alternatively, you can use
+ (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id)delegate
To send high score data, because it is so small, the easiest way is simply to encode it in the URL.
NSString* url = [NSString stringWithFormat:#"http://www.yourserver.com/sethighscores.php?uid=%#;name=%#;score=%d;antifraud=%#", [uid encodeParameter], [name encodeParameter], score, [secureHash encodeParameter]];
Where encodeParameter is a custom category on NSString that encodes URL parameters and secureHash is a string representing a one way secure hashing of the uid, name, score and some secret known to your iPhone app and your web site. You'll need to figure these out on your own or ask separate questions since this is already getting long.
According to NSData writeToURL docs (at least for iPhone OS 2.2.1):
"Since at present only file:// URLs are supported, there is no difference between this method and writeToFile:atomically:, except for the type of the first argument."
Although the docs for NSArray/NSDictionary/NSString do not specifically mention the restriction, it would seem highly likely that the same restriction applies.
So you will have to upload the XML using some other mechanism.
Also, web sites generally are read only, unless you provide specific code on the web server to support uploading.