Access Request Data sent, inside requestFailed Function ASIHttpRequest - objective-c

How to access the (POST)data sent with the request from the requestFailed/requestFinished function.
- (void) abc {
NSString *postString = #"john";
NSURL *url = [NSURL URLWithString:#"http://abc.com"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setPostValue:postString forKey:#"name"];
[request setDelegate:self];
[request startAsynchronus];
}
- (void) requestFinished:(ASIHTTPRequest *)request
{
// Question is whether the request holds the sent post values.
// If it holds. how can we access them.
// i tried using [request valueForKey:#"name"];
// but it won't work.
}

Handling success and failure for multiple requests in delegate methods
If you need to handle success and failure on many different types of
request, you have several options:
If your requests are all of the same broad type, but you want to
distinguish between them, you can set the userInfo NSDictionary
property of each request with your own custom data that you can read
in your finished / failed delegate methods. For simpler cases, you can
set the request’s tag property instead. Both of these properties are
for your own use, and are not sent to the server.
If you need to handle success and failure in a completely different way for each
request, set a different setDidFinishSelector / setDidFailSelector for
each request For more complex situations, or where you want to parse
the response in the background, create a minimal subclass of
ASIHTTPRequest for each type of request, and override requestFinished:
and failWithError:.
That provided me a good solution to handle different requests.

You could try this -
- (void)requestFinished:(ASIHTTPRequest *)request {
NSLog(#"Response %d ==> %#", request.responseStatusCode, [request responseString]);
}
You can also handle other methods if you choose, such as:
- (void)requestStarted:(ASIHTTPRequest *)request;
- (void)requestFailed:(ASIHTTPRequest *)request;
The docs are located at http://allseeing-i.com/ASIHTTPRequest/ and are fantastic.

You can cast your request into a ASIFormDataRequest:
if ([request isKindOfClass:[ASIFormDataRequest class]]) {
ASIFormDataRequest *requestWithPostDatas = (ASIFormDataRequest *)request;
NSArray *myPostData = [requestWithPostDatas getPostData];
}
You will also have to make "postData" accessible with a "getPostData" public function in ASIFormDataRequest.

Related

Objective-C error: No visible #interface declares the selector

I have next to no knowledge of Objective-C, but I'm using Cordova (PhoneGap) to create a web app and needed to manipulate the headers sent by their InAppBrowser plugin.
I have managed to get custom headers to send with the request, I changed the NSURLRequest to a NSMutableURLRequest and then set a custom header like this:
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
[request setValue:#"test" forHTTPHeaderField:#"Accept-Encoding"];
My next problem was passing the headers from the javascript. I added an additional option to the parameters. The parameters need to be passed along between several functions (or methods?) before they get to the place where I can manipulate the headers. I've managed to pass them through several functions, and NSLog is still showing me the correct value that I sent from my javascript, but I've fallen at the final hurdle. I can't pass an additional parameter to my final function!
I have this bit of code here:
[self.inAppBrowserViewController navigateTo:url withHeaders:headers ];
Which calls the navigateTo function that is defined like this:
- (void)navigateTo:(NSURL*)url withHeaders:(NSString*)headers
{
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
[request setValue:#"test" forHTTPHeaderField:#"Accept-Encoding"];
if (_userAgentLockToken != 0) {
[self.webView loadRequest:request];
} else {
[CDVUserAgentUtil acquireLock:^(NSInteger lockToken) {
_userAgentLockToken = lockToken;
[CDVUserAgentUtil setUserAgent:_userAgent lockToken:lockToken];
[self.webView loadRequest:request];
}];
}
}
The problem I have is that the line that calls navigateTo is giving me the error: "CordovaLib/Classes/CDVInAppBrowser.m:149:6: No visible #interface for 'CDVInAppBrowserViewController' declares the selector 'navigateTo:withHeaders:'"
Declare the method in CDVInAppBrowserViewController.h as
- (void)navigateTo:(NSURL*)url withHeaders:(NSString*)headers;

ASIFormDataRequest: unable to send file to server using asynchronous request

I would like to send a file asynchronously to the server; however, it seems like when I do send the request to the server. The server gives me a HTTP code: 200, which is OK but no file is being uploaded to the server.
However, when I leave it to synchronous... it works perfectly. Weird..
Any ideas would be greatly appreciated,
- (void) sendCSVtoServer: ( Session * ) archive_session {
NSLog(#"file name: %#", [archive_session getFile]);
NSURL *url = [NSURL URLWithString:#"http://xx.x.xxx.xxx:3000/xxx/xxxxxxxx"];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue: [archive_session getEmail] forKey:#"email"];
[request addFile: [archive_session getFile] forKey:#"csv"];
[request setDelegate:self];
[request startSynchronous];
}
Thanks!
You are setting a delegate, but you mention you did not implement any of those methods. ASIFormDataRequest extends ASIHTTPResquest, and therefore inherits all its properties.
You will have to implement methods decalred in ASIHTTPRequestDelegate, at least
- (void)requestFinished:(ASIHTTPRequest *)request;
and
- (void)requestFailed:(ASIHTTPRequest *)request;
Maybe others depending on your needs.
Your upload is probably still working (can you check your server?), but, when you send the request asynchronously, you don't have any way to determine if the request has succeeded or failed; right now you are just sedning and forgetting.
All the methods declared in ASIHTTPRequestDelegate.h are marked as optional, which is why the debugger, compiler, and runtime is not complaining.

Using objective-c to get coldfusion generated JSON

By going to a url test.com/test.cfm I am able to output the following:
{"COLUMNS":["OBJID","USERNAME","USERPASSWORD","USERFNAME","USERMI","USERLNAME","COMPANY","TBLCOMPANYID","TBLTITLE","USERADDRESS1","USERADDRESS2","USERCITY","USERSTATE","USERZIP","USERSIGNATUREFILE","USERBUSINESSNUMBER","USERBUSINESSEXT","USERFAXNUMBER","USERCELLNUMBER","USEROTHERNUMBER","USEREMAIL1","USEREMAIL2","USEREMAIL3","DEFAULTPROJECTID","SIGNATURE","SIGNATUREUPLOADBY","SORTORDER","DISABLESTATUS","UUID","SITEID","PROGRAMID"],
"DATA":[[1,"test",11214.0,"admin","","admin","adf Inc.",1,1,"admin","","","California","","","",null,"","","","admin#test.com","","",0,null,null,0,false,"468373c5-1234-1234-1234-3133a2bb1679",62,1]]}
To iterate through this I will first need to get the data using this?
NSMutableData *receivedData;
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"test.com/test.cfm"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:receivedData];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
NSLog(#"test = %#",receivedData);
} else {
// Inform the user that the connection failed.
}
Am I on the right track? The output says null...
You've not assigned anything to receivedData; it'll either be nil (under ARC) or an undefined value which may or may not be nil (under non-ARC). You've created an object that could be used to initiate a URL connection but done nothing with it. You're also probably not getting a valid NSURL because you've failed to specify the URI scheme (ie, the http://).
Probably the easiest thing to do (assuming at least iOS 5 and/or OS X v10.7) would be to use NSURLConnection's +sendAsynchronousRequest:queue:completionHandler: and then NSJSONSerialization to parse the result. E.g.
NSURLRequest *theRequest =
[NSURLRequest
requestWithURL:[NSURL URLWithString:#"http://test.com/test.cfm"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// or just use [NSURLRequest requestWithURL:[NSURL ...]], since the
// protocol cache policy and a 60s timeout are the default values anyway
[NSURLConnection
sendAsynchronousRequest:theRequest
queue:[NSOperationQueue mainQueue]
completionHandler:
^(NSHTTPURLResponse *urlResponse, NSData *data, NSError *error)
{
// catch all for connection errors...
if(
(urlResponse.statusCode < 200) ||
(urlResponse.statusCode >= 300) || // bad status code, e.g. 404
error || // error is non-nil would imply some other error
![data length] // data returned was empty
)
{
// report some sort of connection error
return;
}
NSError *jsonError = nil;
id <NSObject> returnedJSONObject =
[NSJSONSerialization
JSONObjectWithData:data options:0 error:&jsonError];
if(jsonError)
{
// server returned unintelligible JSON...
return;
}
NSLog(#"Got object %#", returnedJSONObject);
// then, e.g.
if(![jsonObject isKindOfClass:[NSDictionary class]])
{
// server didn't return a dictionary
return;
}
NSArray *columns = [jsonObject objectForKey:#"COLUMNS"];
if(![columns isKindOfClass:[NSArray class]])
{
// server returned no COLUMNS object, or that
// object wasn't an array
return;
}
NSLog(#"columns are %#", columns);
/* etc, etc */
}];
The class type checking stuff quickly gets quite tedious if you don't find a way to automate it but that's all validation stuff not directly related to your question.
What the above achieves is that it sends an asynchronous (ie, non-blocking) request for the contents of the URL. Results are accumulated on the main queue (ie, the same place you'd normally do user interactions). Once that entire HTTP operation is complete the code you specified at the bottom is called and that validates and parses the response. It does so synchronously so will block but that's not worth worrying about unless profiling shows it's worth worrying about.
The built-in parser is used for the JSON and everything down to 'Got object' is really just ensuring that the fetch and parse succeeded. It may not be complete — if you can think of anything else that might go wrong then don't assume I've ignored it on purpose.
At that point you just have an object of unknown type but it'll normally be a dictionary or an array because of the fundamentals of JSON. So the example code tests that it really is a dictionary then uses the normal NSDictionary interface to obtain an object for the 'COLUMNS' key. If you attempted to call objectForKey: on an array you'd raise an exception since arrays don't implement that method.
It's then fairly rote — the code checks that the object stored as 'COLUMNS' was an array. Per the JSON rules it could have been another dictionary or a string or one of a few other things. Possibly of interest is that the code calls isKindOfClass: to test that an object was found and that it was an array in a single call; that works because it's explicitly permissible to send any message to nil and the result will always be nil, which looks the same as a BOOL NO.

RestKit: distinguish multiple requests in didLoadResponse:

I'd like to use RestKit and handle several different requests in the same class, i.e. in the didLoadResponse: method. How can I distinguish between the different requests? How do I know which request is finished?
I'm doing the request via
RKClient *client = [RKClient sharedClient];
[client get:#"/....", method] delegate:self];
Then, in the delegate-method
- (void)request:(RKRequest *)request didLoadResponse:(RKResponse *)response {
if (???) // request which gets XY returned
...
else if (???) // request which gets YZ returned
...
}
is that possible?
Sure, the RKClient get: method returns a RKRequest object. Just set a userData to the request and retrieve it later in the delegate.
RKClient *client = [RKClient sharedClient];
RKRequest *request = [client get:#"/....", method] delegate:self];
[request setUserData:#"FirstRequest"];
and check it later in the delegate
- (void)request:(RKRequest *)request didLoadResponse:(RKResponse *)response {
id userData = [request userData];
if ([userData isEqual:#"FirstRequest"]) // request which gets XY returned
...
else if (...) // request which gets YZ returned
...
}
This isn't an exact answer to your question, but I have the feeling that some people will come here wondering how to distinguish multiple requests in didLoadObjects, as I did. The solution is to use isKindOfClass.
For example, I make two HTTP calls when a user logs into my app, and I want to distinguish the object returned from the getUser call from the object returned by getSummary (because if I don't then it crashes). This code checks if the returned object is a "kind of" that particular class, and if so sets the object to a local instance of that object.
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
if ([[objects objectAtIndex:0] isKindOfClass:[APIUser class]]) {
APIUser *apiUser = [objects objectAtIndex:0];
}
else if ([[objects objectAtIndex:0] isKindOfClass:[APIUserSummary class]]) {
APIUserSummary *summary = [objects objectAtIndex:0];
}
}

How to handle different requests using connectionDidFinishLoading in the same delegate?

Whenever I do a curl call using the below code:
NSURL *url = [NSURL URLWithString:requestURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:30];
if (connectionInProgress) {
[connectionInProgress cancel];
}
connectionInProgress = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];
connectionDidFinishLoading is my final destination where I can manipulate the response data and call my next methods to continue with the app . If I hard-code some specific tasks like
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData];
[parser setDelegate:self];
[parser parse];
[someLabel setText:parsedTextFromXMLData];
}
If I need to do another curl call to a different address, wouldn't someLabel setText always get re-set again? Is there a way to make this delegate function behave differently on each curl call? (btw, is connectionDidFinishLoading usually the right place to put the next step of codes?) If so then wouldn't it always get called again by the next curl call?
Have a look at this S.O. post for a recipe concerning NSURLConnection and multiple requests.The suggestion is doing something like this:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (connection == firstConnection) {
// do something
}
else if (connection == secondConnection) {
// do something else
}
}
EDIT: the idea here is that connectionDidFinishLoading is a method of your own delegate (so you write it). In the delegate, you store the address of each connection you create; then, when the connection comes back with the data, you tell which connection it is by comparing its address to the one you stored in the delegate. -END EDIT
Another option you have is using the ASIHTTPRequest framework, which offers a request-based (as opposed to connection-based) delegation mechanism, so each request has got a delegate object to handle the result; or, in other words, the delegate receives a reference to the request, so you can easily tell which request result you are handling.
ASIHTTPRequest offers a bunch of advantages over NSURLConnection. You can read about them in this S.O. post.
There're 2 options to do this:
you can implement a separate class, that will be responsible for handling NSURLConnection delegate stuff and create a separate instance for each request
you can use NSObject key-value methods on NSURLConnection instance for setting up some tag, that will be checked in connectionDidFinishLoading: method
For me, option 1 will be a better approach