Sending Images and data to PHP server-side - ios

It,s my first time trying to send Images & Data to a PHP Server from an app, and I am quite confused as to what's the best way to achieve this.
In my research, I came across AFNetworking library and this base64 library (which I would take suggestion in another one), but I don't know if I can achieve what I want with that or how to implement it.
What I want to do is to send data & images that have a relationship.
Lets say the User has to upload their user details + their picture and their house details + a picture
my JSON would be something like
{
"userDetails": { "name":"jon",
"surname":"smith",
"phone":"123412",
"userPic":"base64pic"
},
"house": { "address":"123 asd",
"postcode":"w2 e23",
"housePic":"base64pic"
}
}
of course that JSON would also have to include security validations.
My problem comes when I would like to avoid using base64 encoding given the 33% size increase and I don't know how I could send the same information to PHP.
I get very confused when trying to send images & data that have a relationship and should be stored taking that relationship into account in the server.
Basically What I am looking for is a way to send the same information but not base64 encoded images but keeping the relationship in the data and trying to send as fewer request as possible. Is it possible? if so How?

look at this example for instance, eveything is pretty self explanatory but ask me if you have any questions
-(void) postStuff{
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:#"https://www.yourdomain.com/"]];
NSDictionary *parameter = #{#"body"#"Anything you want to say!"};
NSMutableURLRequest *request = [httpClient multipartFormRequestWithMethod:#"POST" path:#"api/v1/posts/newpost/" parameters:parameter constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData:imageData name:#"image" fileName:#"image.png" mimeType:#"image/png"];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLOG(#"DONE!!!");
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLOG(#"Error: %#", error);
}];
[httpClient enqueueHTTPRequestOperation:operation];
}

Related

how to handle communicate with couple of different urls with AFNetworking

For my app, I need to communicate with many different urls in the same app, I want to handle this requirements with AFNetworking API, but the AFNetworking examples used a singleton to communicate with one base url, and put the http requests with different relative urls in the operation queue. I am still puzzled with the design using AFNetworking, I think I need to create a couple of singletons to handle different urls, that's definitely a weired design, or I need to re-write the AFHTTPClient to fit my requirements, or I need a networkingMgr to maintain a list of AFHTTPClient, it's hard to decouple the AFHTTPClient with different urls. Could anybody give some suggestions? Thank you very much.
I dont understand why you can't use the following code taken from: CocoaDocs
Using you own NSString for the URL
NSString *myUrlString = [NSString stringWithFormat:#"%#%#",baseUrl,relativeUrl];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:myUrlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];

Using AFNetworking to make API calls

Got a comment to make the main question a separate ticket and separate each question.
I am trying to write code for an SDK. I need to make API calls.
I am using AFNetworking 2.0 to send a POST request to the server:
NSDictionary * params = [self formDictionaryFromCard: card];
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] requestWithMethod:#"POST" URLString: [self apiUrl] parameters: params];
[request addValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request addValue:#"Firefox" forHTTPHeaderField:#"User-Agent"];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
op.responseSerializer = [AFJSONResponseSerializer serializer];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
successBlock(responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
errorBlock(error);
}];
[[NSOperationQueue mainQueue] addOperation:op];
Is this the correct way to use AFNetworking to make API calls for an SDK?
How do I provide support for https?
Is this the correct way to use AFNetworking to make API calls for an
SDK?
Your code will work as-is. There are a few suggestions I'd make to change it.
Main Queue
Although the sample code on CocoaDocs shows mainQueue being used, consider if you want to use a different NSOperationQueue besides mainQueue. Here are a few possible reasons:
If you'll need to go through and find one of your operations later (for example, if you want to cancel/pause uploads.)
If you use mainQueue for anything else, and you don't want the priority of those operations to be compared to the priority of your network operations when the system looks at which operation to start next
If you'd like more than 1 network request to run at a time (for example, if you want to be able to download a photo and post a message at the same time).
You could use AFNetworking's built-in operation queue (on AFHTTPRequestOperationManager):
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager.operationQueue addOperation:op];
You could also use one of your own.
Check for blocks before calling them
You may want to check for the presence of blocks before calling them to void crashes:
if (successBlock) {
successBlock(responseObject);
}
Avoid redundant code
If all or most of your operations will require these customizations to the header, it's probably easier to subclass AFHTTPRequestOperationManager, and override HTTPRequestOperationWithRequest: success: failure: to add your headers there. Then you can use AFHTTPRequestOperationManager's convenience methods (the ones that begin with POST and GET).
Take a look at the documentation for AFHTTPRequestOperationManager.
How do I provide support for https?
For most uses, simply include https in your URL (in your case, in [self apiUrl]). For specific uses, such as to allow invalid certs, or to only accept specific ones, you will need to look into the AFSecurityPolicy class.

Downloading a binary file with RestKit 0.20.x

I have seen a lot of major changes with the RestKit framework in version 0.20.x for the iOS platform.
The one thing I haven't found so far on the web is an example of how to download a binary file with the new version of RestKit.
I need to send a JSON object to a REST service and expect a binary file in return. Would seem simple, wouldn't it but for some reason RestKit only expects JSON (and the common internet content types such as XML) to come back.
The JSON object essentially is a request object telling the service which image it should go and get for me.
Fortunately I have managed to use the underlying AFNNetworking framework to help me with this and leverage the RestKit serializer to produce the request object I needed.
MyRequestClass *request = // ... get my request class instance
RKObjectManager *manager = [RKObjectManager sharedManager];
NSMutableURLRequest *downloadRequest = [manager requestWithObject:request method:RKRequestMethodPOST path:ROUTE_URL_MY_SERVICE parameters:nil];
AFHTTPRequestOperation *requestOperation = [[AFImageRequestOperation alloc] initWithRequest:downloadRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// Use my success callback with the binary data and MIME type string
callback(operation.responseData, operation.response.MIMEType, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Error callback
callback(nil, nil, error);
}];
[manager.HTTPClient enqueueHTTPRequestOperation:requestOperation];

AFJSONRequestOperation / AFHTTPClient fetching old data

So all I'm trying to do is serve up a .json file somewhere (I've tried both my own personal server, and also on AppFog), then performing a GET request using AFJSONRequestOperation on the iOS platform. My code to do this request is as follows:
AFHTTPClient *aclient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:kBaseURL]];
NSURLRequest *request = [aclient requestWithMethod:#"GET"
path:#"voucher.json"
parameters:nil];
AFJSONRequestOperation *op = [[AFJSONRequestOperation alloc] initWithRequest:request];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"%#", (NSString *)responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[op start];
The problem here is that say the .json file initially contained an array of 2 strings. The above code would fetch these objects just fine. Then if I edited the .json file with another 3 strings, rerunning the code would continue to fetch only the 2 old strings.
Only 10-15 minutes after I made the change to the .json file will a refresh give me the updated data. At first I thought this was a caching issue, but setting [client setCachingPolicy:] didn't make any difference.
This issue is present whether I host my .json file on my static server, or running in a Node express server on AppFog. It just keeps returning an old version of the resource requested.
I'm really puzzled about this: so any help would be greatly appreciated!
Make sure the url request is not cacheing the data. I had to implement the following so I would get new json data each time a fetch was made.
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:5];
This is likely a caching issue - you could change the cache policy on the request or append something onto the query string, like the current time.
To do this you could use something like:
[aclient requestWithMethod:#"GET"
path:[NSString stringWithFormat:#"voucher.json?%#", [NSDate date]]
parameters:nil];

AFNetworking POST file fails (echonest?)

I am trying to post a sound to echo nest for file analysis. The POST method is no different than any other method, and I believe it's not echonest dependent.
Here is the documentation. I need the "upload" part.
http://developer.echonest.com/docs/v4/track.html
NSURLRequest *request = [self multipartFormRequestWithMethod:#"POST" path:[NSString stringWithFormat:#"track/upload", self.apiKey] parameters:dictionary constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:data name:#"track" fileName:[path lastPathComponent] mimeType:#"multipart/form-data"];
}];
AFURLConnectionOperation *operation = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"done");
NSLog(#"response: %#", operation.responseString);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", error.description);
NSLog(#"response: %#", operation.responseString);
NSLog(#"headers: %#", operation.request.allHTTPHeaderFields.description);
NSLog(#"operation url: %#", operation.request.URL.absoluteString);
}];
However, I have problems. Here is my response:
{"response": {"status": {"version": "4.2", "code": 4, "message": "track - Missing Parameter: track or url is required with a POST content-type of \"application/x-www-form-urlencoded\" or \"multipart/form-data\""}}}
I thought I have already given the "track" parameter to be the data I initialized. Can anyone help me?
I experienced same problem few weeks ago. And here is what i found.
AFNetworking makes slightly wrong multipart/form-data request.
In AFHTTPClient.m file,
static inline NSString * AFMultipartFormFinalBoundary() {
return [NSString stringWithFormat:#"%#--%#--%#%#", kAFMultipartFormCRLF, kAFMultipartFormBoundary, kAFMultipartFormCRLF, kAFMultipartFormCRLF];
}
They put kAFMultipartFormCRLF twice at the end of final boundary of a request.
But Echonest only except request with one CRLF at the end of multipart/form-data post request.
I don't know which one is exactly the righteous way in terms of HTTP protocol standard, but if you modify that code line (remove a kAFMultipartFormCRLF), i think your code would work well.
FYI, while writing this answer, I explored current AFNetworking Git repo, and found out that this defect has been fixed just 5 days ago. So i guess you can just use latest source code to fix the problem.

Resources