Caching headers in NSURLConnection? - iphone

I am getting a 304 response from an API-call which is fine, because the body did not change. However, a HTTP-header-field in the response has changed! This change is not reflected in NSURLConnection's response object. It's as if the HTTP-headers are loaded from a cached copy of the response locally.
Is there a way to keep 304-caching (based on ETags) while having up to date headers?
NSURLCachePolicy doesn't seem to have a value for that..

I don't think NSURLConnection is going to work for you. I asked the Apple developers in the labs at WWDC '13 about cached responses and custom header values, and they told me to use a lower level connection method (such as CFNetworking) or a third party library which implemented their own (someone told me AFNetworking handles it well).

Related

URLSession cache only

Sometimes I want to get data from the cache only when using URLSession. For example when quickly scrolling in a UITableView, I would like to show images that are already in the cache, but do not fire any HTTP requests. Images are just an example could be anything.
So I'm currently looking into URLSession's CachePolicy but it doesn't support an option to only get valid (not expired, etc) data from cache.
I can look into the URLCache myself, but this also of course returns data that might be expired. Is there some API that can validate a CachedURLResponse? Because then I can do it myself. Or do I have to implement the validating myself.
That's a fairly unusual request. Normally, you're either writing code to operate in an offline mode (in which case you want to pull from the cache whether the cached results are still valid or not) or you are online (in which case you want to fetch new data if it isn't valid).
I would encourage you to really think long and hard about whether you really want to force cache validation if you aren't firing network requests.
That said, if you really want that behavior, there are two ways you can do it:
Use NSURLRequestReturnCacheDataDontLoad and validate the age of the cached response yourself.
Perform the request in a custom session, use NSURLRequestUseProtocolCachePolicy, and in that session, install a custom NSURLProtocol subclass that overrides initWithTask:cachedResponse:client: and startLoading, and calls URLProtocol:didFailWithError: on the provided client at the top of its startLoading method.
The second approach is probably the best option, because you don't have to worry about knowing all the esoteric rules for cache validation. By making the actual load fail, the cache will work normally, but as soon as it actually would start making a network request, your custom protocol prevents that from happening. And because you'll register the protocol only in that specific session (via the protocolClasses array on the session configuration), it won't break networking in other sessions.

How to wait for a certain status of a NSURLConnection

Sorry to bother with yet another NSURLConnection question, adding to the over one thousand already here.
The scenario is as follows. In an iPhone app using dynamically loaded augmented reality features, the user is prompted to download new AR models as these are made available. The models can be several MB large, so the user should be given an indication of the total size of all models to be downloaded before deciding to do so.
To find out how large each file is I want to use an asynchronous NSURLConnection but then to stop the download once I have got the response ([NSURLResponse expectedContentLength]). I can do this in the delegate's connection:didReceiveResponse: method.
My question is, how can I wait until this condition arises? How can I setup the NSURLConnection, let it start asynchronously and then wait until the connection:didReceiveResponse: method is called? I have tried using a NSCondition, letting this wait after setting up the NSURLConnection and in the connection:didReceiveResponse: method signalling the condition. But all this did was to freeze the main thread. Any ideas?
Maybe you could send a HEAD request instead of GET. This may depend on your server set up, but that should get you just the headers, including Content-Length. You ought to be able to use a NSMutableURLRequest so you can change the request method, and then read expectedContentLength on the response as usual.

iOS - Is it possible that [NSData dataWithContentsOfURL:url] would return only part of the bytes of the content?

Will [NSData dataWithContentsOfURL:url] either return the full amount of bytes on success, or nil if something goes wrong?
Is there a chance, that it would return maybe only half of the bytes of the content... perhaps if their internet connection fails halfway through?
If there is a chance that it would return only partial data, is there some other function I could use that would be more reliable and I would be able to know definitely whether they got the full amount of data or not?
I'm not sure about the implementation of -dataWithContentsOfURL: but using a sychronous method like this is not really recommended anyway.
Something based on NSURLConnection is your best bet, but you need to be aware of a few things. Most people don't realize that if the server disconnects while an NSURLConnection is receiving data, it will not cause the download to fail with an error. The -connectionDidFinishLoading: delegate method will be called as normal. Many people get this wrong.
If you want to be sure you have all the data, you need to handle the -connection:didReceiveResponse: delegate method and save the value of [response expectedContentLength]. Then in -connectionDidFinishLoading: you can make sure you received the same number of bytes as expected, and generate an error if not.
There are many free libraries out there based on NSURLConnection like AFNetworking. However you need to beware of bad code. I've just checked the source to AFNetworking and it appears they also do not check for the case where the server sends back less data than the Content-Length header specifies. Also note that the popular ASIHTTPRequest is no longer being actively developed and has received some criticism over its implementation.
I'll leave it up to others to suggest other alternative libraries, but NSURLConnection is the right direction.
If you 'worry' about such a thing I'd recommend using NSURLConnection with it's appropriate delegates.
The async approach (that is NSURLConnection) is imho always better.

How to determine if NSURLConnection:sendSynchronousRequest is returning cached data or not

Wanted to know, because side effect of this could be to alert user that new data was available if didn't get cached data. Bad idea? Was hoping not to have to compute checksum on data and store that for later comparison purposes (although I might be able to use the http response's "last-modified" date instead of a checksum).
The NSURLConnectionDelegate protocol has a connection:willCacheResponse: method. This method is invoked when a NSURLConnection receives and subsequently caches a response. It therefore provides you with a hook in which you could set a flag indicating that a cache is in use. Alternatively if you want to disable caching altogether you can return nil from your implementation.
A point worth noting is that if your delegate manages multiple NSURLConnections you will need to determine which one was the source of the response using the connection: parameter.

Accessing XML data online?

I am just testing an app to get data off our web server, previously I had been using:
NSURL, NSURLRequest, NSURLConnection etc. to get the data that I wanted.
But I have just noticed that if I swap to using XML I can simply do the following and pass the results to NSXMLParser:
NSURL *url = [NSURL URLWithString:#"https://www.fuzzygoat.com/turbine?nbytes=1&fmt=xml"];
Am I right in thinking that if your just after XML this is an acceptable method? It just seems strongly short compared to what I was doing before?
gary
That code only creates a URL object that represents a URL. It doesn't make any request or download any data. You still need to use NSURLRequest and NSURLConnection in order to actually download any data from the server.
Also, stay away from methods like 'initWithContentsOfURL:` and friends, unless you understand that they will block the thread that they are called on until complete. For network services, this method shouldn't be used because it'll block your UI for an indeterminate time, because you can't predict how fast the internet connection will be wherever the app is used.
NSURLConnection's asynchronous request system is exactly what you need. It won't block the UI, and provides a nice encapsulated interface to downloading data from a remote location.
This is definitely the right way to go. There do exist many different connection methods (including my favorite, ASIHTTPRequest) and many, many different xml parsers (including my favorite, KissXML) that are faster or more memory efficient than the Apple built in methods.
But to answer your question, yes, your logic and design pattern is correct.
UPDATE: Because Jasarien seems to think the question talks about asynchronous actions, I will discuss that here. ASIHTTPRequest handles async very very easily. Just check out the quick samples.
Depending on how much XML you get back from the web service, NSXMLParser may not be ideal because the entire XML document has to be read into memory.
Memory is pretty scarce on an iPhone, so using a SAX parser like that in libxml2 is probably better for larger XML files. Instead of reading the entire document into memory, the XML is streamed through and parsed for specific nodes of interest. The memory overhead is lower because less data is stored at once.
Once a node of interest is parsed, an event handler is called to do something useful, like store the node data somewhere.
In this case, take a look at Apple's XMLPerformance sample project for example code.