NSNetwork error after starting NSURLSessionDownloadTask - ios

I use NSURLSession to download files in background. Here is how I start a new NSURLSessionDownloadTask:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[self nextURL]];
_task = [[self downloadSession] downloadTaskWithRequest:request completionHandler:nil];
[_task resume];
After calling resume, I encountered the following error:
nsnetworkd[1557] <Error>: Couldn't add download <SSDownload: 0x16d63a20>: 958545229883046269 to download manager
After this, no delegate methods were called (it seems like the session was killed). The internet connection was ok (Safari was able to open websites).
It occurred only once on iOS 7.0.4. Can someone please explain what could this error mean? Is this an iOS 7.0.x bug or is there a good workaround?

Related

watchOS app stops sending NSURLSessionDownloadTask requests

I have an iOS application and a companion watchOS application. iOS app provides access token for watchOS app when watchOS app starts for the first time. After this watchOS app starts to get data updates from the server periodically by sending NSURLSessionDownloadTask requests.
My NSURLSession is configured following way:
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:[[[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:#".backgroundsession"] stringByAppendingString:[NSUUID UUID].UUIDString]];
config.sharedContainerIdentifier = APP_GROUPS_ID;
self.urlSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
Then I send requests periodically following way:
NSURL *url = [NSURL URLWithString:API];
NSMutableURLRequest *request = [self buildRequestForURL:url andMethod:#"POST"];
NSString *body = [NSString stringWithFormat:<some request params>];
[request setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]];
[[self.urlSession downloadTaskWithRequest:request] resume];
watchOS app successfully receives updates for some time. It successfully receives updates when iOS app is in background mode or even when it is manually unloaded.
But watchOS app stops receiving updates when I lock iPhone screen (right after or after a couple of minutes).
Has anybody experienced the same issue with watchOS apps?
Take a look at This Apple Example - WatchBackgroundRefresh, and my answer to this question. Hopefully that can lead you to some working code.

NSURLConnection randomly fails until device reboot

I have been struggling with an issue where NSURLConnection calls instantly fail. The device needs to be rebooted entirely or Flight Mode needs to be toggled on/off to resolve the problem. Restarting the app (swipe up) alone does not help.
Some facts:
-All URLs are HTTPS, TLS 1.2 compatible with Forward Secrecy. There are no issues with ATS and iOS 9. The error has been present since iOS 7 and remains on 9.2.
-No third party frameworks are used by the app. I use only native NSURLConnection calls that always work, except for when this odd situation occurs.
-No infrastructure/network issues - other devices on same networks (same WiFi for instance) work in the same app at the same time. Going to/from 3G/Wifi makes no difference.
-I always implement willCacheResponse to return nil.
-The service is hosted on AWS Elastic Beanstalk, so some suggested that it might be a DNS caching issue in case of IP address changes - this seems unlikely to me and should trigger multiple errors at once on different devices, which I have never seen.
-The method called is didFailWithError, instantaneously, as if there were no Internet connection on the device at all - all other apps work, however.
-The website that hosts the API used by the app can be browsed with no problems at all times. The website actually makes the same requests to fetch data.
The error code returned is -1003, kCFURLErrorCannotFindHost. I've been following a thread on Git dealing with the same issue to no avail. https://github.com/AFNetworking/AFNetworking/issues/967
I tried using NSURLRequestReloadIgnoringCacheData for all my requests, but that did not help.
With this information, will anyone care to venture a guess what I might be doing wrong? I added the bounty because I have no idea how to approach this problem - especially because it's so inconsistent. And it is definitely not a legitimate error (that is, that the domain could not be found), as the service is operating fine while this happens on random clients.
I create my request with a static method that looks like this. It's been stripped of some non-public info, but basically it just performs a POST request with JSON data. [Controller getSQLHost] just returns a URL - the base domain.
+(NSURLConnection*)initiatePOSTwithJSONDictionary:(NSDictionary*)dictionary toURL:(NSString*)urllocation withDelegate:delegate {
NSMutableDictionary *connectionDictionary = [[NSMutableDictionary alloc] init];
if (dictionary) {
[connectionDictionary setObject:dictionary forKey:#"input"];
}
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:connectionDictionary options:kNilOptions error:nil];
NSURL *url = [NSURL URLWithString:[[Controller getSQLHost] stringByAppendingString:urllocation]];
NSString *postLength = [NSString stringWithFormat:#"%i", (int)[jsonData length]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:jsonData];
return [[NSURLConnection alloc] initWithRequest:request delegate:delegate];
}
Does you delegate implement connectionShouldUseCredentialStorage ? (or respond with YES)
I think the device's keychain is used when this method returns yes which may explain the persisting failure beyond the life time of the running application and why rebooting or otherwise resetting network connectivity "fixes" it. If an authentication failure has been recognized once, it may linger in the key chain for a little while which would then respond immediately without actually going to the server.
What would cause the authentication to register as a failure in the keychain in the first place may depend on a variety of factors. It could be as simple as a typo in the saved password or more convoluted such as some certificate expiration preventing the SSL layer from establishing a secure link.
You're creating NSURLConnections on the current runloop. How many are active at any one time? Have you considered using an NSOperationQueue so that you don't get bitten by load bugs?
Is your delegate thread-safe? If not, that could explain the sporadic-ness of the bug.
You say you don't see the problem often, but others do. Can you borrow their devices and maybe even them and their usage patterns and thus get to see the problem more often?

AFNetworking lib not loading data from cache in offline mode

I am developing a content reading app in which some data is displayed in a tale view and respective detail views.
Now I have already completed the app but there is a small bug.
I am using AFNetworking library for online data load and offline caching.
I have defined caching policy as described by following code.
Reachability *reach = [Reachability reachabilityWithHostname:#"google.com"];
if ([reach isReachable])
{
// Reachable
request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60.0];
}
else
{
request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReturnCacheDataDontLoad
timeoutInterval:60.0];
}
But I am having a small problem that if I load some data online and then turn off the internet connection and close the app.
And afterwords if I restart the app again in offline mode, the cached data should load but it is not happening.
I also have tried changing the caching policies as defined in the following link:
http://blog.originate.com/blog/2014/02/20/afimagecache-vs-nsurlcache/
app deployment target : iOS 6.0
devices : universal
xcode version : 6.1
AFNetworking lib version : 2.0
Any suggestion is appreciated, thanks in advanced

AFNetworking lib not loading data in offline mode in iOS

I am developing a content reading app in which some data is displayed in a tale view and respective detail views.
I have already completed the app but there is a small bug.
I am using AFNetworking library for online data load and offline caching.
I have defined caching policy as described by following code:
Reachability *reach = [Reachability reachabilityWithHostname:#"google.com"];
if ([reach isReachable]) {
// Reachable
request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60.0];
}
else{
request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReturnCacheDataDontLoad
timeoutInterval:60.0];
}
But, I am having a small problem that if I load some data online and then turn off the internet connection and close the app, and afterwords if I restart the app again in offline mode, the cached data should load but it is not happening.
I also have tried changing the caching policies as defined in this link.
app deployment target : iOS 6.0
devices : universal
xcode version : 6.1
AFNetworking lib version : 2.0
Replace all of the code you posted and just set the request cache policy to NSURLRequestUseProtocolCachePolicy.
As it stands, you're currently making a synchronous reachability query on every request. Either that or worse—it's returning immediately, but always returning before a real reachability state can be determined.

How to work with large file uploads in ios?

My app requires upload of video files from users phone which will then be processed on server.
THe problem is the size of the file can go to 200 MB plus and the user won't keep the application open to wait for the file to upload. Since apple doesn't allow apps to run in background for more than a limited time. How can I ensure that my files are uploaded. I am using afnetworking to set up an upload task as given by ios 7 library.
Please if anyone can point me in the right direction or have any solution it would be greatly appreciated. I have banged my head on this for too long. Thanks.
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
[manager setTaskDidSendBodyDataBlock:^(NSURLSession *session,NSURLSessionTask *task ,int64_t bytesSent, int64_t totalBytesSent,int64_t totalBytesExpectedToSend){
CGFloat progress = ((CGFloat)totalBytesSent / (CGFloat)sensize);
NSLog(#"Uploading files %lld -- > %lld",totalBytesSent,totalBytesExpectedToSend);
[self.delegate showingProgress:progress forIndex:ind];
}];
dataTask = [manager uploadTaskWithStreamedRequest:request progress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(#"Error: %#", error);
} else {
}
}];
My request is a normal multipart form request.
Use:
NSURLSessionConfiguration:backgroundSessionConfiguration:
instead of
NSURLSessionConfiguration:defaultSessionConfiguration
From the NSURLSessionConfiguration:backgroundSessionConfiguration: documentation:
Upload and download tasks in background sessions are performed by an external daemon instead of by the app itself. As a result, the transfers continue in the background even if the app is suspended, exits, or crashes.
So in your case, change:
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
to:
NSString *appID = [[NSBundle mainBundle] bundleIdentifier];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:appID];
Implementing application:handleEventsForBackgroundURLSession:completionHandler: on your app delegate will allow your app to be woken up (ie. un-suspended or un-terminated in background mode) when an upload has completed (whether it has completed successfully or not).
Don't get confused with Background Fetching. You don't need it. Background Fetching simply wakes you app up to periodically give your app the chance to fetch small amounts of content regularly. It may however, be useful for restarting failed "background-mode" uploads periodically.
You should use a background session configuration instead if a default session configuration. This ensures that your data transfer will continue in the background once the user has exited your app.
Of course, this is correct as long as the user has background fetching enabled for your app in the Settings app of the device.
Be sure to enable the Background fetch capability on your project settings:
(source: migueldiazrubio.com)
(source: migueldiazrubio.com)
Then implement the application:handleEventsForBackgroundURLSession:completionHandler: in your App Delegate to be informed when the data transfer ends and do whatever you need to do (UI update…) inside your app with the received/sent file. Don't forget to call the completionHandler to inform the system that you have ended your tasks. iOS then will take a screenshot of the active screen of your app and update the one in the iOS 7 multitasking screen.

Resources