AFNetworking with AFJSONRequestOperation and filedata never completes - iphone

I'm using AFNetworking to post json data to my webservice and get a json response back. However now I also want to add multipart formdata to these POST's. If I do this (even without the parameters I added first) the completion block never fires. The progress block DOES fire and I can see that the file uploads correctly.
Does anybody have any experience posting images like this with AFNetworking? I am using the latest AFNetworking source / version.
This is my initial code for posting a dictionary with json in it. This works fine.
NSMutableDictionary *postdata = [[NSMutableDictionary alloc] init];
[postdata setObject:[postdictionary JSONString] forKey:#"request"];
NSMutableURLRequest *jsonRequest = [httpClient requestWithMethod:#"POST"
path:path
parameters:postdata];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:jsonRequest success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {// Success} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {// Failure }];
[operation start];
This is the version of the code for submitting an image (doesn't work)
NSMutableURLRequest *jsonRequest jsonRequest = [httpClient multipartFormRequestWithMethod:#"POST" path:path parameters:postdata constructingBodyWithBlock: ^(id <AFMultipartFormData> formData) {
[formData appendPartWithFileData:imageData name:#"photo" fileName:#"photo.jpg" mimeType:#"image/jpeg"];
[formData throttleBandwidthWithPacketSize:5000 delay:0.1];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:jsonRequest];
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
NSLog(#"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Upload succes");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Upload failed");
}];
[operation start];

I use this code on a number of apps and havent had any problems with it (note I am using the AFJSONRequestOperation and AFRestClient)
set the request
// I have a shared singleton in a separate file APIClient.h
AFRESTClient *httpClient = [APIClient sharedClient];
NSURLRequest *request = [httpClient multipartFormRequestWithMethod:#"POST" path:method parameters:params constructingBodyWithBlock: ^(id <AFMultipartFormData> formData)
{[formData appendPartWithFileData:imageData
name:#"image"
fileName:#"image.png"
mimeType:#"image/png"];
}];
then the operation,
AFJSONRequestOperation *operation = [[AFJSONRequestOperation alloc] initWithRequest:request];
my progress block
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
NSLog(#"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);
CGFloat progress = ((CGFloat)totalBytesWritten) / totalBytesExpectedToWrite * 100;
[SVProgressHUD showProgress:50 status:[NSString stringWithFormat:#"%0.1f %%", progress]];
}];
success block (my API returns a stat -ok or fail, then the data (same as flickr API)
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id response) {
NSLog(#"response string: %#", operation.responseString);
NSString *resultStat = [response objectForKey:#"stat"];
if ([resultStat isEqualToString:#"ok"]) {
//success
// do something with your data that is returned from the API
} else {
//error
NSLog(#"Data Error: %#", response);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", [error localizedDescription]);
}];
[httpClient enqueueHTTPRequestOperation:operation];

cannot see any issues with your code, few possibilities:
try setting short timeout interval and check if request completes
[request setTimeoutInterval:5];
also try commenting out the throttling line and see if it changes anything

Related

Download from Google Drive using OAuth2

I am trying to download from Google Drive using OAuth2, but I am unable to authenticate url.
I get the following error:
2013-08-21 21:22:39.569 LoudCloud[4790:c07] Authorization
successful... 2013-08-21 21:22:40.262 LoudCloud[4790:c07] Error: Error
Domain=AFNetworkingErrorDomain Code=-1011 "Expected status code in
(200-299), got 401" UserInfo=0x98b90f0
{AFNetworkingOperationFailingURLRequestErrorKey=https://doc-0k-bg-docs.googleusercontent.com/docs/securesc/6gj8c1544p7s7pniqjbc4mq6j0fb056i/8bj1nr5shkpsku5m0mde1rr8v7n975ll/1377108000000/00742820994415136760/00742820994415136760/0B0kZ8xc_ZGhbWnZOQUVuTGtObjA?h=16653014193614665626&e=download&gd=true>,
NSErrorFailingURLKey=https://doc-0k-bg-docs.googleusercontent.com/docs/securesc/6gj8c1544p7s7pniqjbc4mq6j0fb056i/8bj1nr5shkpsku5m0mde1rr8v7n975ll/1377108000000/00742820994415136760/00742820994415136760/0B0kZ8xc_ZGhbWnZOQUVuTGtObjA?h=16653014193614665626&e=download&gd=true,
NSLocalizedDescription=Expected status code in (200-299), got 401,
AFNetworkingOperationFailingURLResponseErrorKey=}
CODE:
GTMOAuth2Authentication *auth =
[GTMOAuth2ViewControllerTouch authForGoogleFromKeychainForName:kKeychainItemName
clientID:kClientID
clientSecret:kClientSecret];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[auth authorizeRequest:req completionHandler:^(NSError *error) {
if (error == nil) {
NSLog(#"Authorization successful...");
}
else {
NSLog(#"Authorization failed...");
}
}];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:req];
op.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
[op setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
float percentage = ((float)((int)totalBytesRead) / (float)((int)totalBytesExpectedToRead)) * 100.0;
NSLog(#"Download percentage: %f", percentage);
}];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Successfully downloaded file to %#", path);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[op start];

AFHTTPClient does not send complete data

I am sending a wav file using the following code, however, the process stops at
Sent 131072 of 141359 bytes
So? Is there some problem with the code? Or am i missing something?
NSString *vidURL = [[NSBundle mainBundle] pathForResource:#"M1F1-int24WE-AFsp" ofType:#"wav"];
NSData *videoData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:vidURL]];
NSURL *url = [NSURL URLWithString:#"http://apps2.mobiiworld.com"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
// NSData *imageData = UIImageJPEGRepresentation([UIImage imageNamed:#"Romantic.jpg"], 0.5);
NSMutableURLRequest *request = [httpClient multipartFormRequestWithMethod:#"POST" path:#"/staging/krafttesting/upload.php" parameters:nil constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData:videoData name:#"file" fileName:#"recording.wav" mimeType:#"audio/vnd.wave"];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
NSLog(#"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);
}];
// [httpClient enqueueHTTPRequestOperation:operation];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {NSLog(#"Success");}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", operation.responseString);
NSLog(#"error: %#", error.userInfo);
}];
[operation start];
I got the solution. It seems AFHTTPClient works only when ARC is on. I had ARC off, due to which it was not working - Though i had no errors received.

AFNetworking setImageWithURLRequest download progress

I am using this code to set image to UIImageView.
NSURLRequest *URLRequest = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:imageToLoad]];
[imageView setImageWithURLRequest:URLRequest placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
[cell.image setImage:image];
[cell.activityIndicator stopAnimating];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
// Nothing
}];
But I want to track download progress with that method, is it possbile to do it in setImageWithURLRequest method?
Normally I do this to show loading progress percentage:
[SVProgressHUD showWithStatus:#"Start download..."];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:link]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// success
[SVProgressHUD showSuccessWithStatus:#"Done."];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// failed
[SVProgressHUD showErrorWithStatus:#"Failed."];
}];
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
[SVProgressHUD showWithStatus:[NSString stringWithFormat:#"Downloading... %0.0f%%", totalBytesRead*100*1.0/(totalBytesRead+totalBytesExpectedToRead)]];
}];
Out of the box, no UIImageView+AFNetworking category doesn't have this functionality. However, it can easily be added to by adding this method to the category:
-(void)setDownloadProgressBlock:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))block{
[self.af_imageRequestOperation setDownloadProgressBlock:block];
}
Take a look at this cocoa pod: https://github.com/xmartlabs/XLRemoteImageView . It uses objective-c internals to achieve what you want. I hope it helps you.

iOS 5 - AFNetworking - Uploading Video

For my app, users will be able to record a video or choose a video from their photo album and through a php script upload the video file to the server. To upload the file to the server, I am using AFNetworking. I originally tried to upload the video from the photo album, but since I could not get that to work I added a video (that I know uploads fine through an html front I made for the php script) into the main bundle.
The code is:
NSString *vidURL = [[NSBundle mainBundle] pathForResource:#"test" ofType:#"mov"];
NSData *videoData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:vidURL]];
NSLog(#"The test vid's url is %#.",vidURL);
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL fileURLWithPath: #"http://www.mywebsite.com"]];
NSMutableURLRequest *afRequest = [httpClient multipartFormRequestWithMethod:#"POST" path:#"/upload.php" parameters:nil constructingBodyWithBlock:^(id <AFMultipartFormData>formData)
{
[formData appendPartWithFileData:videoData name:#"file" fileName:#"test.mov" mimeType:#"video/quicktime"];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:afRequest];
[operation setUploadProgressBlock:^(NSInteger bytesWritten,long long totalBytesWritten,long long totalBytesExpectedToWrite)
{
NSLog(#"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);
}];
[operation start];
My PHP script runs fine as I am able to upload the same video via an HTML form. The above code fails to get to the setUploadProgressBlock, however.
Does anything stick out as broken to anyone or is there something else I am missing?
Thanks in advance
This is what I used to fix the problem:
httpClient = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"http://www.mysite.com"]];
NSMutableURLRequest *afRequest = [httpClient multipartFormRequestWithMethod:#"POST" path:#"/upload.php" parameters:nil constructingBodyWithBlock:^(id <AFMultipartFormData>formData)
{
[formData appendPartWithFileData:videoData name:#"file" fileName:#"filename.mov" mimeType:#"video/quicktime"];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:afRequest];
[operation setUploadProgressBlock:^(NSInteger bytesWritten,long long totalBytesWritten,long long totalBytesExpectedToWrite)
{
NSLog(#"Sent %lld of %lld bytes", totalBytesWritten, totalBytesExpectedToWrite);
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {NSLog(#"Success");}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {NSLog(#"error: %#", operation.responseString);}];
[operation start];
Maybe the objects are getting deallocated immediately. Make the AFHTTPClient an instance variable of your class or subclass it and make it a singleton.
More Important: Replace this line:
[operation start];
with:
[httpClient enqueueHTTPRequestOperation:operation];

How do I set the data for a "PUT" request with AFNetworking?

I have started to use AFNetworking and it works well when it gets to making simple "GET"-request. However now I am trying to do a "POST"-request. I use the code below to do the "GET" request. When looking at the puthPath of AFHTTPClient there is no way to set the data to use for the body. My guess is that there is another way of fixing this. I have been looking at the AFHTTPOperation as a way of fixing this. However I am not getting this to work. The problem is that I do not know how to use it with Basic Authentication.
Could somebody give me a hint of how to do a simple "POST"-request with AFNetworking?
AFHTTPClient* client = [AFHTTPClient clientWithBaseURL:ServerURL];
[client setAuthorizationHeaderWithUsername:self.username
password:self.password];
NSString* resourcePath = [NSString stringWithFormat:#"/some/resource/%#",
endPath];
[client getPath:resourcePath
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
// Success code omitted
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Some error handling code omitted
}
];
I did not find any easy way to do this. But I did as recommended and created my own sub-class of AFHTTPClient. In the subclass I implemented the methods below. This makes it possible to perform both POST-request & PUT-requests with my own data.
- (void)postPath:(NSString *)path
parameters:(NSDictionary *)parameters
data:(NSData*)data
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
{
NSURLRequest *request = [self requestWithMethod:#"POST" path:path parameters:parameters data:data];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[self enqueueHTTPRequestOperation:operation];
}
- (void)putPath:(NSString *)path
parameters:(NSDictionary *)parameters
data:(NSData*)data
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
{
NSURLRequest *request = [self requestWithMethod:#"PUT" path:path parameters:parameters data:data];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[self enqueueHTTPRequestOperation:operation];
}
-(NSMutableURLRequest*)requestWithMethod:(NSString *)method
path:(NSString *)path
parameters:(NSDictionary *)parameters
data:(NSData*)data;
{
NSMutableURLRequest* request = [super requestWithMethod:method
path:path
parameters:parameters];
[request setHTTPBody:data];
return request;
}
With AFNetworking 2.0, I just copy code from
- (AFHTTPRequestOperation *)PUT:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
and add a
[request setHTTPBody:data];
Here is it:
NSString* str = [bookDetailLink objectForKey:#"Body"];
NSData* data = [str dataUsingEncoding: NSUTF8StringEncoding];
NSMutableURLRequest *request = [self.manager.requestSerializer requestWithMethod:#"PUT" URLString:bookingDetailUrl parameters:nil error:nil];
[request setHTTPBody:data];
AFHTTPRequestOperation *operation = [self.manager HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *op, NSHTTPURLResponse *response) {
NSLog(#"%#", response);
}
failure:^(AFHTTPRequestOperation *op, NSError *error) {
NSLog(#"%#", error);
}];
[self.manager.operationQueue addOperation:operation];
I am integrating Skyscanner API to our iOS app using AFNetworking.
With AFNetworking 1.3.2 the following code works for me:
NSData *imageData = UIImageJPEGRepresentation(thumb, 0.85F);
AFHTTPClient *httpClient = [[AFHTTPClient alloc]
initWithBaseURL:[NSURL URLWithString:#"https://example.com/"]];
NSMutableURLRequest *request = [httpClient
requestWithMethod:#"PUT" path:#"/foo" parameters:nil];
[request setHTTPBody:imageData];
[request setValue:#"image/jpeg" forHTTPHeaderField:#"Content-Type"];
AFHTTPRequestOperation *operation = [httpClient
HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *op, NSHTTPURLResponse *response) {
NSLog(#"%#", response);
}
failure:^(AFHTTPRequestOperation *op, NSError *error) {
NSLog(#"%#", error);
}];
[operation start];
This results in a PUT request with correct headers, Content-Lenght and general RESTfulness :-)