AFNetworking: How do I measure HTTP request and response times? - ios

Trying to debug AFNetworking in my application. I need to capture the time of both request and response time to get the duration.
The duration will help me to set a reasonable timeout for the GET operation. The default 60 seconds timeout is not enough.
How do I get the request and response time? Is it part of the AFHTTPRequestOperation object?
[[AFHTTPRequestOperationManager new] GET:#"http://www.example.com"
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"startTime={} endTime={}");
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"startTime={} endTime={}");
}
];

Related

How to intercept the response of AFNetworking request in Objective-C?

As titled, and how to check HTTP status of response?
For example, if the server returns http status code 403, I need to send a recall mail request again to take new access token.
Take a look at the below. In the failure block, retry/resend your query X number of times. Be sure to add logic to end retries at some point, so you don't end up with an infinite loop.
AFHTTPRequestOperationManager *operationManager = [AFHTTPRequestOperationManager manager];
[operationManager POST:url
parameters:object
success:^(AFHTTPRequestOperation *operation, id responseObject) {
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (operation.response.statusCode == 403) {
// retry
}
}
];
This answer may also be helpful.

How to refresh or cancel the server call(GET/POST) using AFNetwork?

I have the Textfield USERNAME. Here when the user start to type, for every letter I have to hit server for check "username availability". Now i call it in foreground so it stuck little bit. I could not cancel or refresh the previous connection. Any idea ?
note: I am using AFNetwork sessionManager.
If you are using the AFNetworking as below:
AFHTTPSessionManager *sessionManager = [AFHTTPSessionManager manager];
NSURLSessionDataTask *task = [sessionManager GET:#"http://example.com/resources.json" parameters:nil success:^(NSURLSessionTask *task, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(NSURLSessionTask *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
you can use
[task cancel];
and if you are using AFNetworking as below:
AFHTTPRequestOperationManager *operationManager=[AFHTTPRequestOperationManager manager];
requestOperation=[operationManager GET:#"" parameters:nil success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject) {
} failure:^(AFHTTPRequestOperation * _Nullable operation, NSError * _Nonnull error) {
}];
you can use
[requestOperation cancel];
Your approach is good. but here i suggest something new approach. When you are going to typing At that time for each character why to call web-service and increase the load. Please check below suggestions.
if you are going to check the username, you should call the api after you write at-least 3 letters. because after that only, any word will be able to use as username otherwise those are the characters. This is totally on the functionality requirement which you are implementing.
just wait till user stop typing and then only call web-service. In between is user is typing continuously (till hold for some ammountof time), cancel previous request to call web-service using below code.
[NSObject cancelPerformSelectorsWithTarget:self];
[self performSelector:#selector(sendSearchRequest) withObject:searchText afterDelay:0.1f];

AFNetworking request header missing after being set

I'm attempting to set custom headers on a per-request basis using AFNetworking, but occasionally the headers will seemingly disappear after being set. Below is the code used to make a request...
+ (void) getWithURI: (NSString*) uri header: (NSDictionary*) header success: (NSString*) successCallback failure: (NSString*)errorCallback dispatch: (NSString*)dispatchedId
{
createManagerInstance();
AFHTTPRequestOperation* operation = [manager GET:uri
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
[RestWrapper succeededWithJson:operation.responseString dispatchedId:dispatchedId successCallback:successCallback];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[RestWrapper failedWithJson:operation.responseString dispatchedId:dispatchedId errorCallback:errorCallback];
}];
NSMutableURLRequest* request = (NSMutableURLRequest*)operation.request;
for (NSString* key in header)
{
if([request valueForHTTPHeaderField:key] != nil)
{
[request setValue:header[key] forHTTPHeaderField:key];
}
else
{
[request addValue:header[key] forHTTPHeaderField:key];
}
}
NSLog(#"Headers: %#", request.allHTTPHeaderFields);
[operation start];
}
For 95% of my requests, they go through as anticipated. Sporadically, however, some will fail and indicate a header is missing. This has been confirmed by capturing the requests in question using Fiddler and seeing that the headers are actually missing. Despite this, the console log of request.allHTTPHeaderFields always shows the headers in place.
The only other thing I noticed is that in general Fiddler reports the caching policy as "max-age=0, private, must-revalidate" for each request. However, whenever a request loses the custom headers, it's caching policy is "no-cache".
This is because you're adding HTTP Header fields in wrong way.
You should add it before request. You may try something like this :
+ (void) getWithURI: (NSString*) uri header: (NSDictionary*) header success: (NSString*) successCallback failure: (NSString*)errorCallback dispatch: (NSString*)dispatchedId{
createManagerInstance();
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.requestSerializer setValue:header[key] forHTTPHeaderField:key];
AFHTTPRequestOperation* operation = [manager GET:uri
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
[RestWrapper succeededWithJson:operation.responseString dispatchedId:dispatchedId successCallback:successCallback];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[RestWrapper failedWithJson:operation.responseString dispatchedId:dispatchedId errorCallback:errorCallback];
}];
[operation start];
}

Copying AFHTTPRequestOperation results in error "request body stream exhausted"

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

AFNetworking and configuring HTTP_ACCEPT

The documentation for AFNetworking notes that you should create subclass of AFHTTPClient and use it as a singleton per web-service.
If I have 2 endpoints at www.example.com, one that allows for 'application/json' in HTTP_ACCEPT and another that needs text/html, what parameter would I configure in my singleton AFHTTPClient class so that it configures the correct HTTP_ACCEPT value?
Implementation details:
#interface MyAFHTTPClient : AFHTTPClient
+ (MyAFHTTPClient *)sharedClient;
#end
[[MyAFHTTPClient sharedClient] getPath:#"endPoint_json"
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}]
At a later time, I need to invoke the html endpoint:
[[MyAFHTTPClient sharedClient] getPath:#"endPoint_html"
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}]
It seems that both of these calls cause "HTTP_ACCEPT"=>"application/json" when the server receives the request.
getPath:... and all of those convenience methods construct a request with requestWithMethod:path:parameters:, and then pass that into HTTPRequestOperationWithRequest:success:failure:, which is then enqueued into an operation queue.
If you need to do a one-off request for HTML or the like, do these steps manually rather than using the convenience method: create the request, set the Accept (HTTP_ACCEPT) is not an HTTP header) header to text/html, and then create and enqueue the operation.

Resources