i use AFNetworking to retrieve data from my web service every thing work perfectly, but i want to detect the time out error. I used this code in the failure block of the request but i don't work
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if ([operation.response statusCode] == 408) {
//time out error here
}
}];
The code 408 is normaly for the time out error
Since AFNetworking is build on top on NSULConnection/NSULRSession you can just NSURLErrorTimedOut to check if the error is a timeout error:
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (error.code == NSURLErrorTimedOut) {
//time out error here
}
}];
You where checking the HTTP status code, but since the connection timed out there is not statuscode.
Related
I am using AFNetworking kit to develop an iOS app.
When the app is launching, it requests an access token from the server.
The code is simple as:
__block int status = 0;
[manager GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject)
{
...fetch access token from response json string
status = 1; //break the loop
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
...networking error
}
and also, to make sure the other requests to the server own the access token, I have put a loop to block the thread until the server responses.
while (status == 0)
{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate date]];
}
This worked before, until I imported a third party notification push library. And the async method:
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
is continuing print deviceToken request in console and the access token request never responds anything, even the server actually provides the response.
I have been stuck for day, can someone helps me?
UPDATE I have tried to comment the device token stuff, the AFNetworking request still doesn't work, no success no failure and no timeout:
UPDATE2 Clarify my question. AFHTTPRequestOperationManager have sent a GET request to the server, and server responses. But the AFHTTPRequestOperationManager doesn't receive it, and no success no failure callback as well.
A couple of thoughts:
If you're going to use this pattern:
you really should set status to 1 in the error block, too; and
Apple uses distantFuture in its examples, not date:
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
You might have something else blocking the main thread. Try putting a NSLog inside the while loop and see if you see that loop running. If not, then identify where you're blocking the main thread.
Needless to say, this overall pattern is inefficient. I'd suggest employing asynchronous patterns, such as adopting completion handlers in your own code:
- (void)performSomeURL:(NSString *)url completionHandler:(void (^)(NSDictionary *response, NSError *error))completionHandler{
NSDictionary *parameters = ...;
[manager GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
completionHandler(responseObject, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
completionHandler(nil, error);
}];
}
which could be called like:
[self performSomeURL:url completionHandler:^(NSDictionary *response, NSError *error) {
// use response and error here
}];
// but not here
If you always adopt asynchronous patterns like this in all of your code, never doing anything that blocks the main thread, then not only will your code be more efficient, but you'll never have to fear about deadlocking on the main thread.
I'm getting NSURLErrorDomain error -1012. error in xcode 6.2 , I'm able to get the access token successfully, I tried reseting simulator.. here is the code
-(void)getLinkedInData:(LIALinkedInHttpClient*)client
{
[client getAuthorizationCode:^(NSString *code) {
[client getAccessToken:code success:^(NSDictionary *accessTokenData) {
NSLog(#"Access token data: %#",accessTokenData);
NSString *accessToken = [accessTokenData objectForKey:#"access_token"];
NSLog(#"Access token: %#",accessToken);
[client GET:[NSString stringWithFormat:#"https://api.linkedin.com/v1/people/~?oauth2_access_token=%#&format=json", accessToken] parameters:nil success:^(AFHTTPRequestOperation *operation, NSDictionary *result) {
NSLog(#"current user %#", result);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"failed to fetch current user %#", error.localizedDescription);
}];
} failure:^(NSError *error) {
NSLog(#"Querying accessToken failed %#", error);
}];
} cancel:^{
NSLog(#"Authorization was cancelled by user");
} failure:^(NSError *error) {
NSLog(#"Authorization failed ::::%#", error.localizedDescription);
}];
}
I'm not sure if this'll answer your question, but I've found that when a calling a web service synchronously, which returns a 401 "Not authorised" error, iOS will simply return a vague -1012 error.
iOS: How can i receive HTTP 401 instead of -1012 NSURLErrorUserCancelledAuthentication
So, try to check whether you're having authentication problems, and tackle the problem from there.
I have a method, for authorizing user. I need Basic authorization.
NSString *url = [NSString stringWithFormat:#"%#/rest/api/person/auth", host];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager setRequestSerializer:[AFHTTPRequestSerializer serializer]];
[manager.requestSerializer setAuthorizationHeaderFieldWithUsername:_loginField.text password:_passwordField.text];
[manager setResponseSerializer:[AFJSONResponseSerializer serializer]];
[manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
[self parseResponseForUser:responseObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error %# ",error);
}];
The main problem here is determining error type. I may have error for authorization and error for network connection problem (host is not reachable).
When login and password don't match criteria, failure block runs. For example, If I put wrong password and login I take this error message.:
Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be
completed. (Cocoa error 3840.)" (JSON text did not start with array or
object and option to allow fragments not set.)
How should i catch error types?
Finally found answer, may be it will be helpful for someone. I just needed to use:
NSInteger statusCode = operation.response.statusCode;
And i can catch it like:
[manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"response:%#", responseObject);
[self parseResponseForUser:responseObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSInteger statusCode = operation.response.statusCode;
if(statusCode == 401) {
} else if (statusCode == 404) {
}
}];
In AFNetworking 3.0+ and in the case of an error, you can access the status code in the failure block's error.userInfo object:
failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSHTTPURLResponse *response = error.userInfo[AFNetworkingOperationFailingURLResponseErrorKey];
NSInteger statusCode = response.statusCode;
// Do something with the status code
}];
you can give a try to get code from error and then show messages accordingly.
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSInteger statusCode = error.code;
if(statusCode == -1001) {
// request timed out
} else if (statusCode == -1009 || statusCode || -1004) {
// no internet connectivity
}
}];
similarly you can check for other code.
Here's how I do it.
[self.httpClient GET:#"someAPI"
parameters:parametersDictionary
success:^(NSURLSessionDataTask *task, id responseObject) {
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
NSInteger statusCode = [response statusCode];
switch (statusCode) {
case 404:
break;
default:
break;
}
}];
Improved response of alok srivastava by using NSURLError enum:
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSInteger statusCode = error.code;
if(statusCode == NSURLErrorTimedOut) {
// request timed out
} else if (statusCode == NSURLErrorNotConnectedToInternet || statusCode || NSURLErrorCannotConnectToHost) {
// no internet connectivity
}}];
Here is how to do it in Swift
((NSURLSessionDataTask, NSError) -> Void) = { (sessionDataTask :NSURLSessionDataTask, responseError : NSError) -> Void in
let response = sessionDataTask.response as! NSHTTPURLResponse
switch (statusCode) {
case 404:
// do stuff
case 401:
// do stuff
default:
break;
}
}
It looks like your server might respond with HTML, or it might respond with JSON. But when you type:
[manager setResponseSerializer:[AFJSONResponseSerializer serializer]];
You're telling AFNetworking to expect JSON.
Instead, try telling it to handle a regular HTTP response if it's not JSON:
NSArray *serializers = #[[AFJSONResponseSerializer serializer], [AFHTTPResponseSerializer serializer]];
AFCompoundSerializer *compoundResponseSerializer = [AFCompoundSerializer compoundSerializerWithResponseSerializers:serializers];
[manager setResponseSerializer:compoundResponseSerializer];
Now, if the JSON serializer fails, the request will be passed to the AFHTTPResponseSerializer, which should call your failure block with the appropriate HTTP error code instead of the JSON parsing error.
Incidentally, AFHTTPResponseSerializer is subclass-able, so feel free to take a look at that option if you want more specific behavior.
It happens frequently, that servers may send a response in a different content type than requested IF they are sending an error.
For example when sending a request with JSON as Content-Type and expecting a JSON response from the server, one would specify the following request headers:
Content-Type: application/json
Accept: application/json
When the request fails due to an authentication error, the server may send you a status code of 401 (Unauthorized) plus an optional response which contains relevant diagnostic information.
Strictly, web servers should respect the Accept header, but unfortunately, some don't and will send a "standard" error response in text/html for example. The details should be specified in the API, though.
Your implementation should handle that case gracefully. That is, your response handler must encode (or parse) the response data according the Content-Type of the response, say text/html or ignore it if suitable. More precisely, you always should query the HTTP status code AND the content type and then make an informed decision how you want to treat the response.
See Aron Brager's answer how to solve that issue with AFN.
your Json must be:
{
"access" : "1",
"admin" : "0",
"code" : "constantine2",
...
"positions" : [
{
"departmentID" : "992c93ee-2fa7-4e53-be5f-4e32a45ba5e6",
"departmentName" : "Dev-C++ resources page (libraries, sources, updates...)",
....
}
],
"userid" : "3b660c13-b856-41fa-a386-814a7b43bacc"
}
I'm getting a strage error from AFNetworking even the though the request seems to work fine (as the change actually happens on the site).
Error Domain=NSURLErrorDomain Code=-999 "The operation couldn’t be completed
(NSURLErrorDomain error -999.)" UserInfo=0x1fd4a080
{NSErrorFailingURLKey=http://gcm.greenlightdispatch.com/services/json/}
The strange thing is that it returns this error but it actually doesn't have a problem completing the request (on the server, the information passes through just fine and all).
AFHTTPClient *httpClient = [[[AFHTTPClient alloc] initWithBaseURL:url] autorelease];
[httpClient postPath:#"" parameters:[NSDictionary dictionaryWithDictionary:params]
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success: %#", [responseObject objectFromJSONData]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error.description);
}];
Thanks for the help
This has been answered many times before. The operation is being canceled which is why you get that error.
What to do about an NSURLErrorDomain -999?
Problem
My app lets users upload photos. This works great.
Now, I am trying to implement a "retry" function if the photo upload fails, for example due to a slow connection.
Here's my retry code:
self.operation = [self.operation copy]; // Creates a new operation with the same NSURLRequest
[self.operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// do success stuff
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog("%#", error);
}];
[[MyAFHTTPClient sharedClient] enqueueHTTPRequestOperation:self.operation];
Upon starting, the failure block is called, outputting:
$0 = 0x12636b50 Error Domain=NSURLErrorDomain Code=-1021 "request body stream exhausted" UserInfo=0x12637810 {NSErrorFailingURLStringKey=https://my/long/url/, NSErrorFailingURLKey=https://my/long/url/, NSLocalizedDescription=request body stream exhausted, NSUnderlyingError=0x13046bb0 "request body stream exhausted"}
Question
How do I change my code to restart the image upload correctly?
What I've tried
I think the issue is that operation.request.HTTPBodyStream is an NSInputStream, which cannot be restarted.
The method -[AFURLConnectionOperation connection:needNewBodyStream:] appears to provide a copy of the input stream. I set a breakpoint in there; it's not called when copying or starting the operation, and I'm not sure how to trigger it.
There's some discussion on a similar issue on the AFNetworking GitHub page, but that relates to retrying after authentication failure.
Other info
My URL Request object is created using -[AFHTTPClient multipartFormRequestWithMethod:
path:
parameters:
constructingBodyWithBlock:]
I would try something like this :
-(void)uploadImage:(NSData *)imageData retry:(BOOL)retry
{
AFHTTPClient *myClient = [[AFHTTPClient alloc] initWithBaseUrl:myBaseURL];
NSURLRequest *request = [myClient multipartFormRequestWithMethod:#"POST"
path:myPath
parameters:myParametersDictionary
constructingBodyWithBlock:^(id <AFMultipartFormData> formData){
[formData appendPartWithFileData:imageData
name:myImageName
fileName:myFileName
mimeType:#"image/jpg"];
}];
AFHTTPRequestOperation *operation = [myClient HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *operation, id responseObject) {
// do success stuff
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog("%#", error);
if (retry) {
[self uploadImage:imageData
retry:NO];
}
}];
[myClient enqueueHTTPRequestOperation:operation];
}
Of course the first time you would call it with retry:YES