Jsonobject + Image Multipart AFNetworking - ios

We are trying to send multi-part request to server using AFNetworking. We need to send one json object and two image files. Following is the curl request for same.
curl -X POST http://localhost:8080/Circle/restapi/offer/add -H "Content-Type: multipart/form-data" -F "offerDTO={"code": null,"name": "Merry X'Mas - 1","remark": "25% discount on every purchase","validityDate": "22-12-2014","domainCode": "DO - 1","merchantCode": "M-4","isApproved": false,"discountValue": 25,"discountType": "PERCENTAGE"};type=application/json" -F "image=#Team Page.png;type=image/png" -F "letterhead=#Team Page.png;type=image/png"
I know this should be fairly easy as I've implemented the server as well as android code for same. And my friend is working on iOS part of this. Also I searched a lot on google, but did not get anything useful. So, I know its against the rules of StackOverflow, but can you guys give me the code for same using AfNetworking? If not please redirect me to examples on same lines.
Edit:
Please find below code that we tried:
UIImage *imageToPost = [UIImage imageNamed:#"1.png"];
NSData *imageData = UIImageJPEGRepresentation(imageToPost, 1.0);
offerDTO = [[NSMutableDictionary alloc]init];
[offerDTO setObject(angry)"" forKey:#"code"];
[offerDTO setObject:[NSString stringWithFormat:#"Testing"] forKey:#"discountDiscription"];
[offerDTO setObject:[NSString stringWithFormat:#"Test"] forKey:#"remark"];
[offerDTO setObject:#"07-05-2015" forKey:#"validityDate"];
[offerDTO setObject:#"C-7" forKey:#"creatorCode"];
[offerDTO setObject:#"M-1" forKey:#"merchantCode"];
[offerDTO setObject:[NSNumber numberWithBool:true] forKey:#"isApproved"];
[offerDTO setObject:#"2.4" forKey:#"discountValue"];
[offerDTO setObject:[NSString stringWithFormat:#"FREE"] forKey:#"discountType"];
NSURL *urlsss = [NSURL URLWithString:#"http://serverurl:8180"];
AFHTTPClient *client= [AFHTTPClient clientWithBaseURL:urlsss];
NSMutableURLRequest *request = [client multipartFormRequestWithMethod:#"POST"
path:#"/restapi/offer/add" parameters:nil constructingBodyWithBlock: ^(id <AFMultipartFormData>formData)
{
NSData *myData = [NSJSONSerialization dataWithJSONObject:offerDTO
options:NSJSONWritingPrettyPrinted
error:NULL];
[formData appendPartWithFileData:imageData name:#"image"
fileName:#"image.jpg"
mimeType:#"image/jpeg"];
[formData appendPartWithFileData:imageData name:#"letterhead"
fileName:#"image.jpg"
mimeType:#"image/jpeg"];
[formData appendPartWithFormData:myData name:#"offerDTO"];
}
];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSDictionary *jsons = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:nil];
}
failure:^(AFHTTPRequestOperation operation, NSError error)
{
NSLog(#"error: %#", [operation error]);
}
];

A couple of observations:
Your example is AFNetworking 1.x. AFNetworking 3.x rendition might look like:
NSURL *fileURL = [[NSBundle mainBundle] URLForResource:#"1" withExtension:#"png"];
// If you need to build dictionary dynamically as in your question, that's fine,
// but sometimes array literals are more concise way if the structure of
// the dictionary is always the same.
// Also note that these keys do _not_ match what are present in the `curl`
// so please double check these keys (e.g. `discountDiscription` vs
// `discountDescription` vs `name`)!
NSDictionary *offerDTO = #{#"code" : #"",
#"discountDescription" : #"Testing",
#"remark" : #"Test",
#"validityDate" : #"07-05-2015",
#"creatorCode" : #"C-7",
#"merchantCode" : #"M-1",
#"isApproved" : #YES,
#"discountValue" : #2.4,
#"discountType" : #"FREE"};
// `AFHTTPSessionManager` is AFNetworking 3.x equivalent to `AFHTTPClient` in AFNetworking 1.x
NSURL *baseURL = [NSURL URLWithString:#"http://serverurl:8180"];
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL];
// The `POST` method both creates and issues the request
[manager POST:#"/restapi/offer/add" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
NSError *error;
BOOL success;
success = [formData appendPartWithFileURL:fileURL
name:#"image"
fileName:#"image.jpg"
mimeType:#"image/png"
error:&error];
NSAssert(success, #"Failure adding file: %#", error);
success = [formData appendPartWithFileURL:fileURL
name:#"letterhead"
fileName:#"image.jpg"
mimeType:#"image/png"
error:&error];
NSAssert(success, #"Failure adding file: %#", error);
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:offerDTO options:0 error:&error];
NSAssert(jsonData, #"Failure building JSON: %#", error);
// You could just do:
//
// [formData appendPartWithFormData:jsonData name:#"offerDTO"];
//
// but I now notice that in your `curl`, you set the `Content-Type` for the
// part, so if you want to do that, you could do it like so:
NSDictionary *jsonHeaders = #{#"Content-Disposition" : #"form-data; name=\"offerDTO\"",
#"Content-Type" : #"application/json"};
[formData appendPartWithHeaders:jsonHeaders body:jsonData];
} progress:^(NSProgress * _Nonnull uploadProgress) {
// do whatever you want here
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(#"responseObject = %#", responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(#"error = %#", error);
}];
You are creating an operation here, but never add it to a queue to start it. I assume you do that elsewhere. It's worth noting that AFHTTPSessionManager doesn't support operations like the deprecated AFHTTPRequestOperationManager or AFHTTPClient used to. The above code just starts the operation automatically.
Note, AFNetworking now assumes the response will be JSON. Given that your code suggests the response is JSON, then note that no JSONObjectWithData is needed, as that's done for you already.
Right now your code is (a) creating UIImage; (b) converting it back to a NSData; and (c) adding that to the formData. That is inefficient for a number of reasons:
Specifically, by taking the image asset, loading it into a UIImage, and then using UIImageJPEGRepresentation, you may be making the resulting NSData considerably larger than the original asset. You might consider just grabbing the original asset, bypassing UIImage altogether, and sending that (obviously, if you're sending PNG, then change the mime-type, too).
The process of adding NSData to the request can result in larger memory footprint. Often if you supply a file name, it can keep the peak memory usage a bit lower.

you can pass your NSdictionary directly to manger post block in parametersfield
UIImage *imageToPost = [UIImage imageNamed:#"1.png"];
NSData *imageData = UIImageJPEGRepresentation(imageToPost, 1.0);
NSDictionary *offerDTO = #{#"code" : #"",
#"discountDescription" : #"Testing",
#"remark" : #"Test",
#"validityDate" : #"07-05-2015",
#"creatorCode" : #"C-7",
#"merchantCode" : #"M-1",
#"isApproved" : #YES,
#"discountValue" : #2.4,
#"discountType" : #"FREE"};
NSURL *baseURL = [NSURL URLWithString:#"http://serverurl:8180"];
AFHTTPRequestOperationManager *manager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:baseURL];
[manager POST:#"/restapi/offer/add" parameters:offerDTO constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:imageData name:#"image"
fileName:#"image.jpg"
mimeType:#"image/jpeg"];
[formData appendPartWithFileData:imageData name:#"letterhead"
fileName:#"image.jpg"
mimeType:#"image/jpeg"];
[formData appendPartWithHeaders:jsonHeaders body:jsonData];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"responseObject = %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error = %#", error);
}]
;

Related

Wanting to use the data I get back when using AFNetworking

I am using AFNetworking to get a JSON response. I am getting is as a PhotoPXArray (model I created using mantle). The log output is exactly the data I want. My problem is using the data. How do I go about saving the response data as a variable that can be used elsewhere in my program.
Also, I am using Sculptor to help with serializing.
-(NSArray*) getPhotoForWord:(NSString*)word {
NSArray *results = nil;
NSString *requestString = BASE_URL;
requestString = [requestString stringByAppendingString:#"photos/search?term="];
requestString = [requestString stringByAppendingString:word];
requestString = [requestString stringByAppendingString:CONSUMER_KEY];
NSString *encoded = [requestString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [SCLMantleResponseSerializer serializerForModelClass:PhotoPXArray.class];
[manager GET:encoded
parameters:nil
//success:^(AFHTTPRequestOperation *operation, id responseObject) {
success:^(AFHTTPRequestOperation *operation, PhotoPXArray *responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
return results;
}
#end
Read the Apple documentation regarding blocks and variables. Or you can view this question on SO that will probably also answer your question.
From the Apple docs:
__block variables live in storage that is shared between the lexical scope of the variable and all blocks and block copies declared or
created within the variable’s lexical scope. Thus, the storage will
survive the destruction of the stack frame if any copies of the blocks
declared within the frame survive beyond the end of the frame (for
example, by being enqueued somewhere for later execution). Multiple
blocks in a given lexical scope can simultaneously use a shared
variable.
Use a completion block to get your data out:
- (void)getPhotoForWord:(NSString *)word completionHandler:(void ^(PhotoPXArray *photoArray))completionHandler
{
NSString *requestString = BASE_URL;
requestString = [requestString stringByAppendingString:#"photos/search?term="];
requestString = [requestString stringByAppendingString:word];
requestString = [requestString stringByAppendingString:CONSUMER_KEY];
NSString *encoded = [requestString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [SCLMantleResponseSerializer serializerForModelClass:PhotoPXArray.class];
[manager GET:encoded
parameters:nil
success:^(AFHTTPRequestOperation *operation, PhotoPXArray *responseObject) {
NSLog(#"JSON: %#", responseObject);
if (completionHandler) {
completionHandler(responseObject);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
Then call it like this:
[object getPhotoForWord:#"word" completionHandler:^(PhotoPXArray *photoArray) {
// Do something with photo array.
}];
Note that this call is asynchronous and will complete at some unknown time in the future. Also, you should likely take an NSError argument in the completion block so you can see if you get an error from the request, but I'll leave that to you.

Sending multiple Images to Web server

I am working in an app, where i need to send 3 images to a Web Server. I don't know the perfect method that works fast and efficient.
I have 3 UIImageView that capture image data from camera or photo album. Below,I am using AFNetworking to send 1 image to Web Server.
NSString *imgPath = [[NSBundle mainBundle]pathForResource:#"Default" ofType:#"png"];
NSData *imgData = UIImagePNGRepresentation([UIImage imageWithContentsOfFile:imgPath]);
NSData *imagVIewData = UIImageJPEGRepresentation(imageView1.image,90);
if (imagVIewData) {
AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"http://myurl.com/xxx.php]];
NSMutableURLRequest *myRequest = [client multipartFormRequestWithMethod:#"POST" path:Nil parameters:Nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:imagVIewData name:#"file_upload" fileName:#"123.jpg" mimeType:#"images/jpeg"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:myRequest];
[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 complete");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"%#",operation.responseString);
}];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperation:operation];
}
}
i need someone advice to send 3 different images from 3 UIImageViews. Is it possible with this program or do i need to work through different methods?
any idea?
The majority of the code you have can actually be kept. Why not try putting all the JPEG representations of the images into an array
NSArray *myArrayOfImages = #[Image1,Image2,Image3]
NSArray *myArrayOfNames = #[strings..]
NSArray *myArrayOfFileNames = #[strings..]
Then within the constructing body with block parameter put something like this..
for(int i=0; i < myArrayOfImages.length; i++){
NSData *temp = [myArrayOfImages objectAtIndex:i];
NSString *tempFile = [myArrayOfNames objectAtIndex:i]
NSString *tempFile = [myArrayOfFileNames objectAtIndex:i]
[formData appendPartWithFileData:temp name:tempName fileName:tempFile mimeType:#"images/jpeg"];
}
you could also use a dictionary or whatever data structure you want, point is you just loop over and append within the constructing block.

Uploading images to server

I am trying to upload an image to a server (that is already built) and I am getting errors like Request has timed out. Other methods of sending text and fetch data from the server are working properly. However, sending an image I found it hard to do it.
I am using the following code at the moment:
-(void)uploadImage:(NSData*)image callbackBlock: (void (^)(BOOL success)) callbackBlock
{
NSString *path = [NSString stringWithFormat:#"upload"];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:image, #"image", nil];
[params addEntriesFromDictionary:self.sessionManager.authParameters];
NSMutableURLRequest *request = [self multipartFormRequestWithMethod:#"POST" path:path parameters:params constructingBodyWithBlock: ^(id <AFMultipartFormData>formData){
[formData appendPartWithFormData:image name:#"Image"];
}];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"!!!Response object: %#",responseObject);
callbackBlock(YES);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#",error.description);
callbackBlock(NO);
}];
[self enqueueHTTPRequestOperation:operation];
}
Do you have any idea what the problem is? Can you give me some suggestions or possible errors on the above code.
Thank you very much.
You can send your image as a base64 encoded text... This should work.
You can use this category to create base64 encoded image:
https://github.com/l4u/NSData-Base64

AFNetworking returning NSCFData; issue with registerHTTPOperationClass [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I am new to AFNetworking and am making a call to a simple login api that returns json like:
{"status":"success","data":{"auth_token":"12jt34"}}
I'm doing it via the following but it is returning __NSCFData rather than something that I can manipuate.
NSURL *baseURL = [NSURL URLWithString:#"http://localhost:3000/arc/v1/api/"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
[httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[httpClient defaultValueForHeader:#"Accept"];
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
uname,#"email", pwd, #"password",
nil];
[httpClient postPath:#"login-mobile" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *className = NSStringFromClass([responseObject class]);
NSLog(#"val: %#",className);
}failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error retrieving data: %#", error);
}];
and it outputs:
2013-03-21 14:52:51.290 FbTabbed[21505:11303] val: __NSCFData
but I'd like it for it to be a dictionary that I can manipulate which is how I think it is supposed to work? What am I doing wrong?
[httpClient defaultValueForHeader:#"Accept"];
should be:
[httpClient setDefaultHeader:#"Accept" value:#"application/json"];
Yes, responseObject is a NSData. You can then parse it into a dictionary or array using NSJSONSerialization method JSONObjectWithData:
NSURL *baseURL = [NSURL URLWithString:#"http://localhost:3000/arc/v1/api/"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
[httpClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[httpClient defaultValueForHeader:#"Accept"];
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
uname,#"email", pwd, #"password",
nil];
[httpClient postPath:#"login-mobile" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSAssert([responseObject isKindOfClass:[NSData class]], #"responseObject is supposed to be a NSData"); // it should be a NSData class
NSError *error;
self.results = [NSJSONSerialization JSONObjectWithData:responseObject
options:0
error:&error];
if (error != nil)
{
// handle the error
// an example of the sort of error that could result in a parse error
// is if common issue is that certain server errors can result in an
// HTML error page (e.g. you have the URL wrong, your server will
// deliver a HTML 404 page not found page). If you want to look at the
// contents of the `responseObject`, you would:
//
// NSLog(#"responseObject=%#", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
}
}failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error retrieving data: %#", error);
}];
Obviously, your results object would be a NSDictionary or NSArray, depending upon the type of response you get from your API.
What am I doing wrong?
NSStringFromClass() returns the name of the class you pass in as an NSString object.
If you want to make a dictionary out of the returned JSON string, then you have to parse it, for example using the NSJSONSerialization class.

issue With AFNetworking?

I Wanna Get Data From PhP Page
My Code Is
NSURL *url = [NSURL URLWithString:#"http://avicenna-apps.com/harleychatget.php"];
AFHTTPClient * Client = [AFHTTPClient clientWithBaseURL:url];
[Client defaultValueForHeader:#"Accept"];
[Client getPath:#"" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"Data %#",responseObject);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error retrieving data: %#", error);
}];
The Output Is
Data <32352020 32303133 2d30312d 31302030 383a3234 3a353620 2041686d 65642020 68656c6c 6f206865 6c6c6f20 3c627220 2f3e3234 20203230 31332d30 312d3130 2030383a 32343a35 30202041 686d6564 20203c62 72202f3e 32332020 32303133 2d30312d 31302030 383a3234 3a343920 2041686d 65642020 3c627220 2f3e3230 20203230 31332d30 312d3130 2030383a 32343a34 38202041 686d6564 20203c62 72202f3e 32312020 32303133 2d30312d 31302030 383a3234 3a343820 2041686d 65642020 3c627220 2f3e200a 0a>
Any Help Please ?
That is binary data. You should convert it.
Try NSString *response = [[NSString alloc] initWithData:(NSData*)responseObject encoding:NSUTF8StringEncoding]; to get the response as string
or simple:
NSString *response = request.responseString;

Resources