AFNetworking HTTP PUT Request - objective-c

I have the following API call:
URL: /api/some-call
Method: PUT
PARAMS: No params
Its just a simple PUT method. I am trying to use AFNetworking to do that and unfortunately, I am failing. Here's what I have right now:
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
NSMutableURLRequest *req = [httpClient requestWithMethod:#"PUT" path:#"" parameters:nil];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:req];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success");
} failure: ^(AFHTTPRequestOperation *operatn, NSError *error) {
NSLog(#"Failure");
}];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];
This is however, not working. Why is that? Furthermore, what is path supposed to be in a PUT request? I've tried several things and this is what I have now at the end, which I believe should be close to what is correct.
One last question: AFNetworking does not use ARC. Does that mean I still need the autorelease at the end of the NSOperationQueue statement?
EDIT:
Here is error NSLog: Failure Error Domain=com.alamofire.networking.error Code=-1011 "Expected status code in (200-299), got 409" UserInfo=0x7a91fb0 {NSErrorFailingURLKey=*the url*/api/some-call, NSLocalizedDescription=Expected status code in (200-299), got 409}

Well. You are getting a 409 error code.
Quoted from http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html :
10.4.10 409 Conflict
The request could not be completed due to a conflict with the current
state of the resource. This code is only allowed in situations where
it is expected that the user might be able to resolve the conflict and
resubmit the request. The response body SHOULD include enough
information for the user to recognize the source of the conflict.
Ideally, the response entity would include enough information for the
user or user agent to fix the problem; however, that might not be
possible and is not required.
Conflicts are most likely to occur in response to a PUT request. For
example, if versioning were being used and the entity being PUT
included changes to a resource which conflict with those made by an
earlier (third-party) request, the server might use the 409 response
to indicate that it can't complete the request. In this case, the
response entity would likely contain a list of the differences between
the two versions in a format defined by the response Content-Type.
Which means the error is caused by your server not with your code. unless you have provided some wrong parameters.
Well. As for the question regarding the "what is the path supposed to be in PUT".
Normally I'll put baseURL as the domain name of the server.
Which is something like
http://localhost
then i'll put the path to be something like
#"the/rest/of/the/api/url"
then it's easier to switch between development and production servers with just a switch of a baseURL. :)
And for your last question, "AFNetworking does not use ARC. Does that mean I still need the autorelease at the end of the NSOperationQueue statement?"
Does that mean your project is using ARC with AFnetworking, or AFNetworking WITHOUT ARC.
if it's ARC with AFNetworking, you don't have to. Take a look at this
https://github.com/AFNetworking/AFNetworking#arc-support
if it's non-ARC with AFNetworking, you basically have to do all the memory management yourself. :)
Hit me up again if you need more info and i'll edit accordingly. :)
Hope i've helped in someway.

Related

Right way to implement HTTP post / Get in iOS

I am quite new to iOS, trying best way to implement HTTP post / Get communication.
Problem:
I want to make a multiple api calls and each calls will have its separate response. I am trying to write common network utils, Ideally it will take api url, make call and return data to caller. What is the right way to achive it?? I found moderate level of debate and fans for each approach.
Option 1:
dispatch_async(aQueue,^{
...[ make a sync network request get data back]
--- perform operation on data
--- then pass proceed data UI or set it in model.
dispatch_async(dispatch_get_main_queue()
}
Option 2:
-(NSString *) postData:(NSDictionary *)data serverUrl:(NSString *)targetUrl
-- call post data method with seperate delegate for each caller
-- start async request
-- on DidFinishedLaunching or OnError check delegate & then
return response back to callback
Thanks for your inputs.
I think your first option is not good. It is going to block the pooled thread for a long period of time which is not advisable. When implementing Multithreading in any environment pooled threads provided by the system should not be used for long running processes. Second synchronus network call are not really advised and it has its own pitfalls.
Your second option is more viable. An improvement that you may be able to do is to perform the work that happens in the did finish launching to a GCD thread and after the processing send the data on the main thread
There is a wonderful library available, called AFNetworking, which is very easy to implement.
It uses blocks, which greatly simply communication of data between classes (does away with delegates), and is asynchronous.
Example usage is below:
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:"www.yourwebsite.com/api"]];
NSDictionary *params = #{
#"position": [NSString stringWithFormat:#"%g", position]
};
[client postPath:#"/api" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
As simple as that! Result is available directly within the class that calls the HTTP Post or Get method.
It even includes image and JSON requests, JSON deserialization, file download with progress callback, and so much more.

Best way to parse SSDP Discovery in Objective C

I am working on a very simple application to discover a device using SSDP and I am trying to find the easiest way to parse the response from this command. I am trying to avoid having to do a bunch of NSString or regular expressions manipulations.
I have tried the following two approaches:
Approach 1:
Using GCDAsyncUdpSocket, I am able to successfully send the discovery command and get the following response:
HTTP/1.1 200 OK
Cache-Control: max-age=300
ST: roku:ecp
USN: uuid:roku:ecp:1234567890
Ext:
Server: Roku UPnP/1.0 MiniUPnPd/1.4
Location: http://192.168.XX.XX:8060/
This looks like a regular HTTP response, but using GCDAsyncUdpSocket, I am getting the response as an NSData object, which I can easily convert to an NSString. However, what would be ideal is to somehow cast this to an NSHTTPURLResponse and then use its methods to get the field values. Any idea if this can be done?
Approach 2:
I have tried using a regular NSURLRequest to try to send this command and then I would be able to get an NSHTTPURLResponse back. However, I keep on getting an error because the SSDP discovery command requires me to send this request to port 1900.
I use the following code to send the "HTTP" request, I know it is not strictly HTTP, but I thought it may be an easier way to send this UDP command as the requirements look very similar to HTTP.
NSURL *url = [NSURL URLWithString:#"http://239.255.255.250:1900"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
[request setHTTPMethod:#"M-SEARCH *"];
[request setValue:#"239.255.255.250:1900" forHTTPHeaderField:#"Host"];
[request setValue:#"\"ssdp:discover\"" forHTTPHeaderField:#"Man"];
[request setValue:#"roku:ecp" forHTTPHeaderField:#"ST"];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];
if (connection)
{
NSLog(#"Connection success!");
}
else
{
NSLog(#"Connection failed!");
}
When I do this, the connection is successful, but I get the following error in the didFailWithError delegate for NSURLConnection:
failed Error Domain=NSPOSIXErrorDomain Code=47 "The operation couldn’t be completed. Address family not supported by protocol family" UserInfo=0x8875940 {NSErrorFailingURLKey=http://239.255.255.250:1900/, NSErrorFailingURLStringKey=http://239.255.255.250:1900/}
This error only happens if I use port 1900, if I leave this out or use another more HTTP friendly port such as 8080, then this works, but obviously the device I am trying to discover does not respond correctly unless it gets the request in port 1900.
Thanks for any help you can provide.
Port 1900 is fixed for SSDP. All UPnP devices are fixed to listen on this port and the connecting nodes expect that. You can't change it. It's a part of the UPnP design goal to "work out of the box". Furthermore, my Cocoa expertise is very limited, but i think that NSHTTPURLResponse won't make things simpler for you. [NSHTTPURLResponse allHeaderFields] returns NSDictionary, which means that you don't know anymore what was the original order of the header fields. And you need to know what was coming after Ext: which is a meta-header.
I suggest either parsing the response yourself, which shouldn't be much more complicated than one cycle, separating the response by lines and then splitting by :. Or instead of trying to roll your own SSDP handshake, use ready made Cocoish library like Upnpx, Cyberlink or Platinum. It might feel like inappropriately heavy artillery just for the discovery phase, but i wonder what else you would do with the device after that, other than actually trying to invoke some actions on the device.

Uploading From App to Server in IOS

I know that conventionally for an app to interact with the internet, it must use a web service to exchange information. However, how would one upload data(photos, text, audio recordings etc.etc.) from app to server(which holds data for all user accounts)? I know some people use an email-to-server tactic from research but even then it sounds ineffective and slow. How do apps such as Instagram upload so fast? I am trying to replicate that sort of uploading. Please guide me in the right direction.
Thanks for the help!
You should definitely look into AFNetworking. Here is an example of my uploading an image to a php web service:
NSData *imageData = UIImagePNGRepresentation(pageImage);
AFHTTPClient *client= [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:#"http://www.SERVER.com"]];
//You can add POST parameteres here
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
author, #"author",
title, #"title",
nil];
NSMutableURLRequest *request = [client multipartFormRequestWithMethod:#"POST" path:#"/PATH/TO/WEBSERVICE.php" parameters:params constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
//This is the image
[formData appendPartWithFileData: imageData name:#"cover_image" fileName:#"temp.png" mimeType:#"image/png"];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
//Setup Upload block to return progress of file upload
[operation setUploadProgressBlock:^(NSInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
float progress = totalBytesWritten / (float)totalBytesExpectedToWrite;
NSLog(#"Upload Percentage: %f %%", progress*100);
}];
//Setup Completeion block to return successful or failure
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *response = [operation responseString];
NSLog(#"response: [%#]",response);
//Code to run after webservice returns success response code
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", [operation error]);
//Code to Run if Failed
}];
[operation start];
Edit- Also I use MBProgressHUD to display to the user the uploading on longer uploads.
As you might know, upload speed is always bound to the speed of the connection type you're using. Even the best upload technique will be slow when the connection is slow (GPRS for example, or EDGE, even 3G can be slow if network coverage is not good).
To upload large sets of data faster/better one thing you could do is compressing the data you're sending using ZIP or any other file compression format you wish or even develop you own compression algorithm (you might not want to do that ;-)).
If you want to reduce the overhead of HTTP/HTTPS connections for example, you can write your very own protocol for data exchange, implement it on the client/server side and upload faster. This will be a lot of work as you have to do all the implementation work not only for the protocol itself as you need to add security etc. But even if you choose to create a protocol, as said in the beginning, it will be slow if the connection is slow.
Update: A presenatation by Mike Krieger (Co-Founder of Instagram) where he covers your question just crossed my way https://speakerdeck.com/u/mikeyk/p/secrets-to-lightning-fast-mobile-design?slide=1.
The reason why you think it's so fast is, that they're updating the UI before the request (the Upload in this case) even completes. This is what Mike describes as "being optimistic". If the request fails you can still notify the user, but in the meantime make him feel productive and act like the request completed successfully.
This is a pretty open ended question but here are a few things to look at:
"Uploading fast" depends on the user's connection and server bandwidth so I won't get into that.
You can upload photos (and other files) by creating NSData objects and attaching them to a POST request. There is already a ton of sample code for uploading NSData but to convert a UIImage you will do the following:
NSData *imageData = UIImagePNGRepresentation(image);
You can do this using the built in Cocoa classes (NSMutableURLRequest) and with 3rd party networking classes (such as AFNetworking - just scroll down to file uploads).
When I send simple data to my webserver, I use the following approach: Use the ASIHttpRequest framework for connecting to your sever. Send the data in HTTP Post body, which is easy to do in the ASIHttpRequest framework. You will want to convert your data to either XML or JSON(use the SBJson framework for this) before sending it. I then write php scripts that parse the json or xml and then input this data into my database with custom SQL scripts. I can give you code snippets if you need them for any of these procedures...
It seems to me that, with your first sentence, you've basically answered your own question.
You need something on your server to receive the files and then you write client code to match. It could be as simple as ftp or as complex as a custom protocol depending on the security and control that you need.

Webserver NSURL NSURLConnection

Ok, I'm trying to get a file from my webserver. But I'm getting kinda confused about some stuff. When I use NSURL I can get my xml-file with an url like this: "localhost...../bla.xml". But I'm also trying to test some things... Like... What will happen to my app if I have an open connection to the webserver, and I lose connection to internet? The above method with the NSURL, I haven't really established any connection where it always is connected right? or should I use be using NSURLConnection?
Maybe it's a little confusing, because I'm confused. I hope someone can give me some info I can research about.
Thanks.
Take a look at NSURLConnection Class.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html
Create connection object and set a timeout value, if you lose the connection or the connection times out NSURLConnection delegate method: - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error gets called and you would be notified of that event.
You might also use NSURLConnection sendSynchronousRequest method, but its strongly discouraged to use that method as it would block the thread its running.
Are you trying to access the content of a file? If so, you would use the following.
NSError *error;
NSURL *url = [NSURL URLwithString:#"localhost/file.html"];
NSString *filecontents = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
The NSString object filecontents would contain your string.
You wouldn't be able to loose connection in such a short time. If there is no connection, an error would be applied to error.
EDIT:
If you wanted to constantly stay connected to a server, that is a different story.
You have have to use C's send() and recv() functions, which you can read about here.
I don't know much about it, and I'm learning it myself, so you should ask someone else on how to set up a server. But you will need to have another program running simultaniously.

RestKit. RKObjectManager and queing

I have 2 fairly simple question.
I have 2 mapped requests sent i close succession to each other, called
by MAIN thread.
First request:
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:#"SomePathToServer"delegate:self]
Second request:
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:#"SomeOTHERpathtoServer" delegate:self];
My question is:
Are they automatically queued by the object manager?
When i run them now the first request will trigger a rather large syncing communication with the webservice. The second request i fired off in the midst of that communication and is not handled/Recieved by RestKit.
If i run my app again, my code detect that the syncing is done, and now the second request is handled - Data is recieved and mapped.
Do i have to manually add my managed requests to a queue?
I havent found anything about it on the net, so if i have to manually
queue it, i wonder if someone has an example or directions to a guide.
I have only found queing examples for simple requests, and i have no
idea on how to put the First and Second request into the queue - if
needed.
Help is much appreciated.
Thomas
RKRequestQueue will do the job. You can add to the queue either RKObjectLoader or RKRequest
Here is the example:
RKRequestQueue *queue =[[RKRequestQueue alloc] init];
queue.delegate = self;
queue.concurrentRequestsLimit = 1;
queue.showsNetworkActivityIndicatorWhenBusy= YES;
[queue addRequest:[[RKObjectManager sharedManager] objectLoaderWithResourcePath:#"resource" delegate:self]];
[queue addRequest:[RKClient sharedClient] requestWithResourcePath:#"Another Resource "delegate: self]];
[queue start];