NSURLCaching with dataTask fails to read Cache back with NSURLRequest when offline - ios

I am very confused by iOS caching system. It should be straightforward but it not (for me). I am writing an database App which allows for external web access to a limited range of additional reference material in the form of web pages. Sometimes a user might need access to the external web pages while in the field where there is no WiFi or Cell data. Simple, I thought, use URLCaching while iterating through the set of external pages during a time when the internet is available. Then, use a request policy of NSURLRequestReturnCacheDataElseLoad.
So, I created a shared cache in the AppDelegate, then a singleton session:
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.URLCache = self->_myCache;
sessionConfig.requestCachePolicy = NSURLRequestReturnCacheDataElseLoad;
sessionConfig.HTTPMaximumConnectionsPerHost = 5;
session = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:nil];
Then fill the cache using dataTask (since a trial of downloadTask did not fill cache):
NSURLSessionDataTask *myTask = [[NSURLSessionDataTask alloc] init];
do {
myTask = [self.session dataTaskWithRequest:req];
myTask.taskDescription = searchName; //Iterating several values of searchName
[myTask resume]; } while ...searchNames...
This fills the cache as evidenced by cache.currentDiskUsage and cache.currentMemoryUsage. Run the web read loop after initial fetches does not further increase size of cache.
Here is the problem:
Running this code fragment with internet off fails to read from cache
NSURL *nsurl=[NSURL URLWithString:#"https:searchName"];
NSURLRequest *nsrequest = [NSURLRequest requestWithURL:nsurl cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:60.0];
[_webView loadRequest:nsrequest];
in addition, once cache is filled, trying to repeat the initial round of fetches with internet off, leads to
TIC TCP Conn Failed [1:0x6000028ce580]: 1:50 Err(50)
Is there no cache interoperability between HTTP if filled with dataTask and reading back from cache with NSURLRequest? What am I doing wrong? Is there a different approach to solve my design goal?

After three days of experimentation, I am able to create a cache of about three hundred web pages (with associated embedded images) and read them back from persistent memory, across launches of the app.
The only approach to accessing the cache is to use the NSURLCache cachedResponseForRequest: method. Using other forms of accessing a URL with appropriate CachePolicy do not seem to work.
There does seem to be an unfortunate side effect of the cachedResponseForRequest: method in that it only passes the original html document to wkwebview, and none of the linked resources which are verified to also be in my cache.
Does anyone have a suggestion to get this limit?

Related

Cannot get background URL Session to return

I have tried and failed many times to get some inherited code to successfully send a background session request. When in the foreground, it works flawlessly, but in the background it will either never return, or, in some cases, receive a auth challenge.
The code to create the request is pretty boilerplate:
NSURLSessionConfiguration *sessionConfig =
[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
sessionConfig.sessionSendsLaunchEvents = true;
sessionConfig.discretionary = false;
sessionConfig.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
sessionConfig.timeoutIntervalForResource = 60 * 60 * 24; //One day. Default is 7 days!
/* Create session, and optionally set a NSURLSessionDelegate. */
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
NSURLComponents *urlComponents = [NSURLComponents new];
urlComponents.scheme = #"https";
urlComponents.host = #"jsonplaceholder.typicode.com";
urlComponents.path = #"/posts/1";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[urlComponents URL]];
request.HTTPMethod = #"PUT";
NSLog(#"Making request: %#", request);
/* Start a new Task */
NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
[task resume];
But I never get anything back. I do have the required background modes (probably)
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>
and my delegate is delegating everything
<NSURLSessionDelegate, NSURLSessionDataDelegate,NSURLConnectionDataDelegate,NSURLConnectionDelegate,NSURLSessionTaskDelegate>
Any help would be appreciated- I'm pretty sure I'm missing just one line of code somewhere. I even went so far as to create a GitHub repo just to test this code out- it schedules a Local Notification to allow you to run the session task in the background.
GitHub repo for BackgroundSender
In the app delegate's handleEventsForBackgroundURLSession, you are doing nothing with the completion handler. You should:
save copy of the completionHandler;
start the background NSURLSession with that identifier; and
when done handling all of the delegate methods, call the saved completion handler; we often do that in URLSessionDidFinishEventsForBackgroundURLSession.
By the way, you shouldn't use data tasks with background sessions. You generally should use a download or upload task.
Also, those background modes are not necessary for background NSURLSession. Background fetch is designed for a different problem, namely periodically polling to see if your server has data available. If you need that, then by all means, use the fetch background mode, but realize that this is a separate topic from merely performing background requests.
See Fetching Small Amounts of Content Opportunistically in the App Programming Guide for iOS: Background Execution for a discussion of background fetch, and compare that to the Downloading Content in the Background section.
Likewise, the remote-notification key is unrelated, too. As the docs say, remote-notification is used when "The app wants to start downloading content when a push notification arrives. Use this notification to minimize the delay in showing content related to the push notification."

iOS dropbox sdk / core api - listing public folder contents without authorization

I've tried checking on stackoverflow and on the api docs, but couldn't find any info pertaining to this particular question.
What I'm trying to do is getting the public folder contents of a particular dropbox account (like the /metadata function of the api), without the need to authorize/link to dropbox.
I know "/metadata/link" allows you to get metadata of a particular link without user authorization, but I couldn't find a way to get regular metadata and file listing...
Actually it doesn't even need to be a public folder (as I've read recently that dropbox discourages developers regarding relying on public folders for apps), it can just as well be a regular folder through its shared link or anything of the kind...
I'm using api v1 (but can consider changing to v2 if this is impossible with v1), although I'm not even sure if this is at all possible.
Thank you!
For future reference, if anyone needs this as well, here's how I did it:
NSString *parameters = [NSString stringWithFormat:#"?link=%#&client_id=%#&client_secret=%#",fileUrl, appKey, appSecret];
NSString *sharedPath = [NSString stringWithFormat:#"%#/%#",#"https://api.dropbox.com/1/metadata/link", parameters];
NSURL *url = [NSURL URLWithString:sharedPath];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
theRequest.timeoutInterval = 5.0;
theRequest.HTTPMethod = #"POST";
NSData * resData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:nil error:nil];
Use NSJSONSerialization to parse resData and refer to the api documentation for json fields, and that's it.
For the sake of simplicity showing it here I used synchronous request, but it can just as easily be done with asynchronous connection delegates.

How does - [NSURLCache cachedResponseForRequest:] actually get the matching cache?

How does -[NSURLCache cachedResponseForRequest:] works? Is it works like key-value pair where the key is the content of the NSURLRequest object (which includes the NSURL object, cache policy, time out interval, HTTPBody, HTTPHeaders)? If so then if any of the above field is different then we could not retrieve the cache.
For example, assuming the NSURLRequest has everything staying the same except the HTTPBody, which one request has the body of:
{
a = 1;
}
while another request has the body of:
{
a = 2;
}
Am I be able to get the cache of former request using the latter request?
IIRC, only the URL is taken into account. If header or body data dfferences matter to you, you'll need to implement a custom cache. The URL Loading System Programming guide should be of some help, should you decide to go down that path.

iOS Deciding to stop accessing server after fixed time

My question might have a very easy answer but I am not going anywhere on Google with the keywords I am using. Let me explain, it surely will be clear to one of you.
In my app, I am uploading a string to a server. If the connection is successful, the user gets a "done" message and if not, I tell him what to do next.
I was testing this afternoon with the "no network simulator" and it works well, but I had to wait may be 90 seconds before I get the message "no connection available". I don't know for how long it tries to connect in real life, but I found the wait really annoying and wouldn't like my user to be in such a situation.
My question is : I want the app to try and connect to the server for say 20 seconds and if after that time it hasn't succeeded, it stops trying and the user decides if he wants to try a second time or not ?
My apologies in advance, English isn't my mother tongue.
Thank you for pointing me to the right direction.
You should set timeout interval in session configuration
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.timeoutIntervalForRequest = 20;
configuration.timeoutIntervalForResource = 20;
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
// in case if you are using AFNetworking
// AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];

Couldn't get Google custom search results using JSON

I'm developing an application that makes use of Google custom search api as a feature. I added a new custom search and included some sites to search, I was able to do a search and get the results I need, but all of a sudden it stopped returning results. It returns an empty array instead. I didn't alter anything, but it looks like I have a limited number of queries, am I right?
Here's the line of code responsible for query a search word:
NSString *search = [NSString stringWithFormat:#"https://www.googleapis.com/customsearch/v1?start=1&ie=utf8&key=XXXXXX&cx=XXXXX&q=SEARHWORD&alt=json"];
url = [NSURL URLWithString:[search stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url];
NSURLConnection *searchConnection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
Then the normal steps of parsing the results.
if it was working fine, and then stopped all of a sudden, you may have hit the limit.
You can double check by copy-pasting
https://www.googleapis.com/customsearch/v1?start=1&ie=utf8&key=XXXXXX&cx=XXXXX&q=SEARHWORD&alt=json
in a browser and seeing the response

Resources