I am using NSURLSessionDownloadTask to pull down a simple text file from my goDaddy server using the following code:
-(void)getTheInternetFile
//Fire up the downloadTask to pull the file down from my web server.
NSURLSessionDownloadTask *getTheFile = [session downloadTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.myserver.com/utility/file.txt"]]
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error)
{
if(error)
{
NSLog(#"Can't do what I want as we have an error %#", [error localizedDescription]);
}
else
{
NSLog(#"It worked");
}
}];
[getTheInternetFile resume];
}
The problem I am having is that this works really well...sometimes. Other times I get an error message that states: "A server with the specified hostname could not be found".
As the URL is hard coded and never changes, I am at a loss to how this can even happen. Unless goDaddy is letting me down, and the server has all of a sudden become unavailable part of the time, I am at a loss. For what it's worth this just started acting this way today, so maybe it is goDaddy.
So it turns out it was goDaddy's server having issues, despite their website reporting that there were none. It took 15 minutes on hold to find out that my code is fine.
Related
In my app I use following methods to POST/GET data from a remote server.
postData = [self sendSynchronousRequest:request returningResponse:&response error:&error];
- (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
{
NSError __block *err = NULL;
NSData __block *data;
BOOL __block reqProcessed = false;
NSURLResponse __block *resp;
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable _data, NSURLResponse * _Nullable _response, NSError * _Nullable _error) {
resp = _response;
err = _error;
data = _data;
reqProcessed = true;
}] resume];
while (!reqProcessed) {
[NSThread sleepForTimeInterval:0];
}
*response = resp;
*error = err;
return data;
}
I have basic error handling for no network connectivity, and app directs users to no network connectivity viewController. But I would like to account for situations when for instance server is down or data format of api has changed, Just wondering how would I catch such errors and prevent the app from crashing.
A couple of general points:
Don't do requests synchronously. Pass in a block with code that should run when the request completes. There's no reason to tie up a thread waiting for a completion handler to run, and it is even worse if that thread is basically continuously running as yours is.
If you absolutely must do that (why?), at least use a semaphore and wait on the semaphore. That way, you aren't burning a CPU core continuously while you wait.
The way you avoid crashing is to sanity check the data you get back, e.g.
Check the length of the data and make sure you didn't get nil/empty response data.
Assuming the server sends a JSON response, wrap your NSJSONSerialization parsing in an #try block, and if it fails or throws an exception, handle the error in some useful way.
Generally assume that all data is invalid until proven otherwise. Check to make sure the data makes sense.
If you're in charge of the server side, consider passing an API version as an argument, and as you modify things in the future, make sure that incompatible changes occur only when responding to clients that request a newer API version (or, alternatively, send a response that tells the client that it needs to be upgraded, and then refuse to provide data).
I'm working on the CS193p coursework from Stanford (I'm not a Stanford student I'm just trying to learn), assignment number 5, where we have to get images from Flickr using calls to the Flickr api. I'm only trying to download a list of the pictures and print them out to the console, but I'm getting an NSURLErrorDomain with code -1005. The error message I am printing out is below:
error: Error Domain=NSURLErrorDomain Code=-1005 "The operation couldn’t be completed. (NSURLErrorDomain error -1005.)" UserInfo=0x7f9449c83df0 {NSErrorFailingURLStringKey=https://api.flickr.com/services/rest/?method=flickr.places.getTopPlacesList&place_type_id=7&format=json&nojsoncallback=1&api_key=4f9c3155b34836b2ac15318d98b93f3a, NSErrorFailingURLKey=https://api.flickr.com/services/rest/?method=flickr.places.getTopPlacesList&place_type_id=7&format=json&nojsoncallback=1&api_key=4f9c3155b34836b2ac15318d98b93f3a, _kCFStreamErrorDomainKey=1, _kCFStreamErrorCodeKey=57, NSUnderlyingError=0x7f9449c7fca0 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1005.)"}
The Flickr API call is happening in the following method:
NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
NSURLSessionDownloadTask *downloadTask = [urlSession downloadTaskWithURL:[FlickrFetcherHelper URLforTopPlaces] completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
NSArray *topPlaces;
if (!error) {
topPlaces = [[NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:location] options:0 error:&error] valueForKeyPath:FLICKR_RESULTS_PLACES];
}
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(topPlaces, error);
});
}];
[downloadTask resume];
I'm using xCode 6, not sure if that has anything to do with the problem. The same code was working a couple days ago, and I don't think I changed anything that would mess it up, but it clearly has.
Are you using the iOS8 (iPhone 6 for example) simulator? If so, try changing to iPhone 5s. I was having the same problem and switched my simulator and all worked well again. I tried this after reading the following feedback: https://github.com/AFNetworking/AFNetworking/issues/2314
Switch to iOS Simulator and do "Reset Content and Settings ..." from top menu.
I'm trying to download a small file, about 5.5kb.
Half of the time it only downloads half of the json, and then of course fails parsing that data. The other half of the time everything works completely normal.
I have a good internet connection.
Im using AFNetworking 2.0. I got it off of the main branch today (1/16/2014).
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:[NSString stringWithFormat:#"%#Physicians/Types/?os=1",BaseUrl] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
NSLog(#"Error: %#", operation.responseString);
}];
Here is the exact error, but i already know its a parsing error:
Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (No string key for value in object around character 4480.) UserInfo=0x15ecd010 {NSDebugDescription=No string key for value in object around character 4480.}
This is the sample from their website. And like I said it works half of the time.
Do I need to set some sort of chunk size? Is it because their Github page says the current build is failing?
Interestingly enough, it seems to always fail at the exact same point in the file. I can tell this because in the error, I output the response string.
I am not able to reproduce this in any browser, or in my old app which used asihttprequest
***Update 1/28/2014
I downloaded the current version today, which is passing build, and it still fails. When I save out to disk what it does receive it's 5,391 bytes. Again, it fails at the same point each time. In the middle of the work dentist. There aren't any weird characters there.
since a few days I have problems with RestKit 0.20
RestKit is included via CocoaPods so it is up to date. I am developing an app for iOS7 and this problems happen on all real and simulated devices.
When I am loading a resource like an image or a pdf file like this:
RKObjectManager *objectManager = [self.restManager objectManager];
[objectManager setAcceptHeaderWithMIMEType:#"image/jpeg"];
[[objectManager HTTPClient] getPath:path parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completeBlock) {
UIImage *image = [UIImage imageWithData:responseObject];
completeBlock(image);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (failBlock) {
failBlock(error);
}
}];
I get this error message:
T restkit.network:RKObjectRequestOperation.m:148 GET 'https://***/attachment/11787':
request.headers={
Accept = "*";
"Accept-Language" = "en-US";
Authorization = "***";
"User-Agent" = "***";
}
request.body=(null)
E restkit.network:RKObjectRequestOperation.m:174 GET 'https://***/attachment/11787' (200 OK) [1.0048 s]:
error=Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." UserInfo=0xf7ba400 {NSErrorFailingURLStringKey=https:/***/attachment/11787, NSErrorFailingURLKey=https://***/attachment/11787, NSLocalizedDescription=The network connection was lost., NSUnderlyingError=0x1605c930 "The network connection was lost."}
response.body=(null)
But this is not the common case for all resources but for only a few. The other resources get loaded at the same time (The requests were added to the queue at the same time).
Once this error happends for a resource, the resource will never get loaded again but failing with the same error message.
I cleaned the cache and deleted the app, so it shouldn't be a problem with caching or something like that. And there is no difference if http or https is used.
Similar to this question (without solution): Restkit not work on iOS 7
Do you have any ideas, what I am doing wrong or what causes this behaviour?
All the server connections in my app goes the same way:
dispatch_async to a 2nd thread.
sendSynchronousRequest
fail/success blocks on main thread.
The following is not the full code, just the concept:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *url = [NSURL URLWithString:#"someURL"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if (error)
{
dispatch_async(dispatch_get_main_queue(), ^{ _failureBlock(error); });
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{ _successBlock(data); });
});
Well the app works great! I can use it for thousands of server connections, and it always works when I run a fresh load of the app.
The issue starts when the following occurs:
Send the app to BG.
Wait some time...
The app get killed by the iOS.
Open again, the iOS loads the whole app from scratch.
(see splash screen followed by the first screen, not the one I left to BG..)
In that scenario I get NSURLConnectionErrors!! All kind of them! Code 1003, Code 1009, Code 9... Just name it!
I get errors for all server connections that starts right when the app loads. Any other connections after that works fine! including the refresh of the ones that got failed!
It almost feel like i'm trying to reach my server too quick or too early, BUT I do check and pass a reachabillity test before intiating a connection.
Could really use some help here. Thanks in advance.
UPDATE: As suggested below, tried to use sendAsynchronousRequest - gave me the exact same behaviour. (err: 1001 this time).
OK got it! The appDelegate built the tabBar wich loaded all viewControllers wich sent some NSURLConnections... So for the iOS thought it's too long to wait for the responses and failed them all! (No idea way the first launch is ok and only after the app killed its not)
Anyway, I changed all server loads to perform with selector with 0.1 delay. That way the appDelegate could finish running and all is GOOD! :)
You should look at using this method and send the data Asynchronously
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
the NSOpertationQueue mainQueue uses the mainthread however allowing you to update UI also on the main thread i.e an activityindicator for example..