To begin with, I have looked up how to make a post request and have read multiple thread and docs about how to create one, however my data doesn't seem to be working.
I have two fields say x and html that i want to make to the call callname.
The GET form of this would be www.someserver.com/callname?x=something&y=something
Here is what my POST code looks like so far:
NSString *baseURLString = #"http://www.someserver.com/callname"
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[[NSURL URLWithString:baseURLString] standardizedURL]];
NSString *fields = [NSString stringWIthFormat:#"x=%#&html=%#",x,htmlSource];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:[NSData dataWithBytes:[fields UTF8String] length:strlen([fields UTF8String])]];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
The NSURLConnection Delegate Methods implemented as such
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
NSLog(#"Data Received");
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"Error: %#" , error);
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSLog(#"Connection Finished");
}
Did i miss something? My code looks pretty identical to most of the examples I found aside from the choice of encoding. Does it matter that I am passing in html source code as the value of y? Any tips or hints will be appreciated. I am very new to iOS and html handling in general so excuse my lack of knowledge on the subject. Thanks for your time!
You must implement the delegate protocol for NSURLConnection to get the response. You haven't posted any of that code, so I would assume this means you didn't implement the relevant methods.
One potential issue is that you missed to properly percent encode the parameters:
When using parameters like in your code with a content type application/x-www-form-urlencoded, I would suggest to create your parameters as (unencoded) NSString key/value pairs, create a NSDictionary object and use the following helper method described in the answer here (How to send multiple parameterts to PHP server in HTTP post) which creates a properly encoded parameter string which you can add to the body.
I believe your form URL encoded is not correct. I believe the solution is the following
[serviceRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
If you want to UTF-8 encode your values, you need to do it before, like for example,
NSString *postLength = [NSString stringWithFormat:#"%d", [_xmlDoc length]]; //Calculating the Content Length
NSData *postData = [_xmlDoc dataUsingEncoding:NSUTF8StringEncoding]; // preparing XML to be sent in POST
Notice you encode the string of POST data ahead of time.
Hope that this helps!!
class Requests {
class func loginRequest(userName:String, password:String, completion: #escaping ((JSON?, String?) -> ()) )
{
var request = URLRequest(url: URL(string: "your URL")!)
request.httpMethod = "POST"
//parameters
let postString = "user_login=\(userName)&user_pass=\(password)"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) {
data, response, error in guard error == nil else {
print(error!)
completion(nil, error!.localizedDescription)
return
}
guard let data = data else {
completion(nil, "location not found")
return
}
let jsonData = JSON(data)
completion(jsonData, nil)
}
task.resume()
}
}
Your code works for me. Did you remember to run [connection start]; ?
Related
I need some help with a LoginViewController.
Basically I have a small app, and I need to post some data to the app and Im new to POST and JSON. If I can get some help and understanding that would be highly appreciated. Below are some requirements im working with. My .m file is labled as LoginViewController. This is what I have so far
-(void)setRequest {
#pragma mark NSURLConnection Delegate Methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// A response has been received, this is where we initialize the instance var you created
// so that we can append data to it in the didReceiveData method
// Furthermore, this method is called each time there is a redirect so reinitializing it
// also serves to clear it
_responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the new data to the instance variable you declared
[_responseData appendData:data];
}
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse*)cachedResponse {
// Return nil to indicate not necessary to store a cached response for this connection
return nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// The request is complete and data has been received
// You can parse the stuff in your instance variable now
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// The request has failed for some reason!
// Check the error var
}
-(void)PostRequest{
// Create the request.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://dev.apppartner.com/AppPartnerProgrammerTest/scripts/login.php"]];
// Specify that it will be a POST request
request.HTTPMethod = #"POST";
// This is how we set header fields
[request setValue:#"application/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
// Convert your data and set your request's HTTPBody property
NSString *stringData = #"some data";
NSData *requestBodyData = [stringData dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPBody = requestBodyData;
// Create url connection and fire request
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
}
I dont know if I'm even setting this up right. I saw many hTTP posts and what not, but im still confused on how I write this syntax and do I need to add anything additional.
I need to:
Send an asynchronous POST request to "some url"
The POST request must contain the parameters 'username' and 'password'
Will receive a JSON response back with a 'code' and a 'message'
Display the parsed code and message in a UIAlert along with how long the api call took in miliseconds
The only valid login is username: Super password: qwerty
When a login is successful, tapping 'OK' on the UIAlert should bring us back to the MainMenuViewController
I'm assuming the methods inside methods are a typo.
Unless you have a particular reason to implement all those delegate methods, you're probably better off using either
NSURLSessionDataTask *task =
[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
// Code to run when the response completes...
}];
[task resume];
or the equivalent using NSURLConnection's sendAsynchronousRequest:queue:completionHandler: method if you still need to support iOS 6 and earlier and/or OS X v10.8 and earlier.
But the big thing you're missing is the encoding of the request body. To do that, you'll probably want to use URL encoding and specify the appropriate MIME type for that as shown here:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/URLLoadingSystem/WorkingwithURLEncoding/WorkingwithURLEncoding.html
Basically, you construct a string by string concatenation in the form "user=ENCODEDUSERNAME&pass=ENCODEDPASSWORD" where the two encoded values are constructed like this:
NSString *encodedString = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(
kCFAllocatorDefault,
(__bridge NSString *)originalString,
NULL,
CFSTR(":/?#[]#!$&'()*+,;="),
kCFStringEncodingUTF8);
Do not be tempted to use stringByAddingPercentEscapesUsingEncoding: and friends. They will do the wrong thing if your strings contain certain reserved URL characters.
I would suggest that you try working with AFNetworking Library.
You can find the code here.
And a very good tutorial here.
You can do like that for this.
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request addValue:#"YourUsername" forHTTPHeaderField:#"Username"];
[request addValue:#"YourPassword" forHTTPHeaderField:#"Password"];
[NSURLConnection
sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// TODO: Handle/Manage your response ,Data & errors
}];
-(IBAction)registerclick:(id)sender
{
if (_password.text==_repassword.text)
{
[_errorlbl setHidden:YES];
NSString *requstUrl=[NSString stringWithFormat:#"http://irtech.com/fresery/index.php?route=api/fresery/registerCustomer"];
NSString *postString=[NSString stringWithFormat:#"name=asd&email=sooraj&phonenumber=8111&password=soorajsnr&type=1&facebookid=&image_path="];
// _name.text,_email.text,_mobile.text,_password.text
NSData *returnData=[[NSData alloc]init];
NSMutableURLRequest *request=[[NSMutableURLRequest alloc]initWithURL:[NSURL URLWithString:requstUrl]];
[request setHTTPMethod:#"POST"];
[request setValue:[NSString stringWithFormat:#"%lu", (unsigned long)[postString length]] forHTTPHeaderField:#"Content-length"];
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
resp=[NSJSONSerialization JSONObjectWithData:returnData options:NSJSONReadingMutableContainers error:nil];
c=[[resp valueForKey:#"status" ]objectAtIndex:0];
b=[[resp valueForKey:#"message"]objectAtIndex:0];
I have the following code to get data from server;
-(void)loginForFaceBook
{
GTMOAuth2ViewControllerTouch *viewController;
viewController = [[GTMOAuth2ViewControllerTouch alloc]
initWithScope:#"https://www.googleapis.com/auth/plus.me"
clientID:#"27615...6qdi60qjmachs.apps.googleusercontent.com"
clientSecret:#"Fs8A...u2PH"
keychainItemName:#"OAuth2 Sample:
Google+"
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
[[self navigationController] pushViewController:viewController
animated:YES];
}
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)auth
error:(NSError *)error {
if (error != nil) {
// Authentication failed (perhaps the user denied access, or closed the
// window before granting access)
NSLog(#"Authentication error: %#", error);
NSData *responseData = [[error userInfo] objectForKey:#"data"]; //
kGTMHTTPFetcherStatusDataKey
if ([responseData length] > 0) {
// show the body of the server's authentication failure response
// NSString *str = [[NSString alloc] initWithData:responseData
// encoding:NSUTF8StringEncoding];
// NSLog(#"%#", str);
}
// self.auth = nil;
} else {
// NSString *authCode = [NSString alloc]in;
NSMutableURLRequest * request;
request = [[NSMutableURLRequest alloc] initWithURL:[NSURL
URLWithString:#"http://api.kliqmobile.com/v1/tokens"]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60] ;
NSLog(#"%#",auth);
NSLog(#"ho gya success %# :::: %# :::: %#", auth.accessToken,
auth.refreshToken, auth.code);
NSMutableURLRequest * response;
NSError * error;
request.URL = [NSURL URLWithString:#"http://api.kliqmobile.com/v1/tokens"];
NSString *post = [NSString stringWithFormat:#"
{\"token\":\"%#\",\"secret\":\"%#\",\"service\":\"%#\",\"handle\":\"%#\"}",
auth.accessToken,auth.code,#"google",nil];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding
allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d",[postData length]];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded"
forHTTPHeaderField:#"Content-Type"];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:postData];
error = nil;
response = nil;
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request
delegate:self];
[connection start];
}
I have implemented the NSURLConnection delegtes method and data is printing well like this
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
NSMutableURLRequest * response;
NSError * error;
NSLog(#"Did Receive Data %#", [[NSString alloc]initWithData:data
encoding:NSUTF8StringEncoding]);
NSMutableURLRequest * requestContacts;
requestContacts = [[NSMutableURLRequest alloc] initWithURL:[NSURL
URLWithString:#"http://api.kliqmobile.com/v1/contacts"]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60] ;
[requestContacts setHTTPMethod:#"GET"];
[requestContacts setAllHTTPHeaderFields:headers];
error = nil;
response = nil;
NSData* data1 = [NSURLConnection sendSynchronousRequest:requestContacts
returningResponse:&response error:&error];
NSLog(#"WE GET THE REQUIRED TOKAN DATA %# :: %# :: %#", [[NSString alloc]
initWithData:data1 encoding: NSASCIIStringEncoding], error ,response);
}
but after that my app get crashed and it is giving following error;
[NSHTTPURLResponse release]: message sent to deallocated instance 0xcb51070.
please suggest me how to do this.
A couple of thoughts:
What is the intent of your didReceiveData method? There are a bunch of issues here:
You really shouldn't be doing a synchronous network request in the middle of a NSURLConnectionDataDelegate method.
You shouldn't be doing synchronous requests at all, but rather do them asynchronously.
What is the connection between receiving data and your creation of this new request? You're not using the data in the request, so why do it here?
The typical pattern is:
The didReceiveResponse should instantiate a NSMutableData object in some class property.
The only function of didReceiveData should be to append the received data to the NSMutableData. Note, this method may be called multiple times before all the data is received.
In connectionDidFinishLoading, you should initiate any next steps that you take upon successful completion of the request. If you wanted to do start another asynchronous network request when the initial request is done, do that here.
In didFailWithError, you obviously handle any failure of the connection.
When you call connectionWithRequest, you should not use the start method. Only use start when you use initWithRequest:delegate:startImmediately: with NO for the startImmediately parameter. Otherwise the connection starts automatically for you and you're only starting it a second time.
Unrelated to your original question, but your creation of post string cannot be right. You're missing a parameter value. Even better, rather than creating JSON manually, use NSDictionary and then use NSJSONSerialization to make the NSData object containing the JSON from this dictionary. That's much safer:
NSDictionary *params = #{#"token" : auth.accessToken,
#"secret" : auth.code,
#"service" : #"google",
#"handle" : #""};
NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject:params options:0 error:&error];
Clearly, supply whatever you need for the handle value.
A tangential process-related observation, but I'm wondering if you're taking advantage of everything Xcode offers. For example, your declaration of response as a NSMutableURLRequest but then using that as a parameter to sendSynchronousRequest should have generated a compiler warning. The same thing is true with your stringWithFormat for your post string (my third point). That should have generated a warning, too.
Neither of these are immediately relevant, but I wonder if you are failing to heed any other compile-time warnings. These warnings are your best friend when writing robust code and I would recommend resolving all of them. To go a step further, you should also run the project through the static analyzer ("Analyze" on "Product" menu, or shift+command+B), and resolve anything it points out, too.
I'm trying to make my own Request class I intend to use throughout my app. Here is the code I've been coming up with so far.
-(IIWRequest *)initAndLaunchWithDictionnary:(NSDictionary *)dictionnary
{
self=[super init];
if (self) {
// Create the request.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://xxxxx.com/app/"]];
// Convert data
SBJsonWriter *jsonWriter = [[SBJsonWriter alloc] init];
NSString *jsonData = [jsonWriter stringWithObject:dictionnary];
NSLog(#"jsonData : %#",jsonData);
NSData *requestData = [jsonData dataUsingEncoding: NSUTF8StringEncoding];
request.HTTPBody = requestData;
// This is how we set header fields
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%d", [requestData length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody: requestData];
// Create url connection and fire request
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
[self activateNetworkActivityIndicator];
if (connection) {
NSLog(#"Connection");
} else {
NSLog(#"No connection");
}
}
return self;
}
I have included NSURLConnectionDelegate. I'd like to fire the connection callbacks such as did finished or did fail back to the function mentioned before. The goal of all that is to get only one method to call in the end looking like :
-(IIWRequest *)initAndLaunchWithDictionnary:(NSDictionary *)dictionary inBackgroundWithBlock:^(BOOL succeeded){}
Any idea ? Thanks !
Use block method of NSURLConnection class it will reduced your functionality as well sendAsynchronousRequest:queue:completionHandler:
Read this doc.
I would hardly suggest you to use one of the currently existing libraries for calling URLs. One of the best I know is AFNetworking https://github.com/AFNetworking/AFNetworking. There is lot of examples and its easy to use and I am sure you should go with it.
Anyway, if you want to build your own class I would suggest you to read post written by Kazuki Sakamoto here NSURLConnection and grand central dispatch.
Regards
If you are using the iOS 7, I recommend A LOT you to use NSURLSession classes, this new network api is really amazing and simple.
Anyway, to answer your question, you just need to hold the reference of callback in your class and call it when you receive some response from the server.
To hold the reference, you can do something like this:
// in your .h file
typedef void (^ResponseBlock)(BOOL success);
// in your .m, create a class extension and put declare the block to use it for callback
#interface MyClass ()
{
ResponseBlock callback;
}
// You can store reference using equal like this
- (void)myMethodRequestWithResponseBlock:(ResponseBlock)responseBlock
{
callback = responseBlock;
// statements
}
// And finally, you call back block simple like this:
callback(success);
Again, use NSURLSession api if you can, you will simplify your work.
I hope this may help you.
Cheers!
I'm working with ASIHTTPRequest but I have a responseData empty.
The response is supposed to be a JSON response and when I try to do this in the browser, it works perfectly.
Here is my code :
- (IBAction)simpleURLFetch {
NSURL *url = [NSURL URLWithString:#"http://url/url/url"];
ASIHTTPRequest* request = [ASIHTTPRequest requestWithURL:url];
request.timeOutSeconds = 30;
[request setDelegate:self]; // I correctly set the ASIHTTPRequestDelegate in my .h file
[request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)azrequest {
NSString *responseString = [azrequest responseString];
NSData *responseData = [azrequest responseData];
NSLog(#"repo : %# / %#", responseData, responseString);
}
And the log displayed this : "repo : (null) / <> /"
Maybe, I'm not using the result with the good way.. Is there a solution to have my JSON response ?
Regards,
Sébastien ;)
The problem is that you're not getting data back, not that you're accessing the data incorrectly. The URL in your code is clearly invalid, and I suspect that's not the one you're using. Either way, the request fails to get any data, so there's nothing to log.
If you have implemented the delegate method:
-(void)request:(ASIHTTPRequest *)request didReceiveData:(NSData *)data
Then, responseData will be null. Because the API will handover the data handling to the application.
After I have read the answer for this question I have found that using reachabilityWithHostName does not work with a URL such as this one: mySite.com/service.asmx , is there anyway to check reachability against this URL using reachabilityWithHostName or any reachability class method ?
thanks so much in advance.
The Reachability class and -reachabilityWithHostname: is designed to be a quick, fail-fast mechanism to determine whether you have basic network connectivity to the host. If you need to verify that a particular URL can be downloaded, you need to be looking at using NSURLConnection to retrieve the contents of the URL in order to verify that it is truly available.
Depending on whether you need to do this in the foreground or background, you can either use the simple-but-blocking:
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
or you can use the more complicated method of creating an NSURLConnection object, setting up a delegate to receive responses and wait for those responses to come in.
For the simple case:
NSURL *myURL = [NSURL URLWithString: #"http://example.com/service.asmx"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: myURL];
[request setHTTPMethod: #"HEAD"];
NSURLResponse *response;
NSError *error;
NSData *myData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error];
If you receive back a non-nil myData, you've got some kind of connectivity. response and error will tell you what the server responded to you (in the case of response and if you received a non-nil myData) or what kind of error occurred, in the case of a nil myData.
For the non-trivial case, you can get good guidance from Apple's Using NSURLConnection.
If you don't want to stall your foreground process, you can do this two different ways. The above documentation will provide information on how to implement the delegate, etc. However, a simpler implementation would be to use GCD to send the Synchronous request on a background thread, and then message yourself on the main thread when you are done.
Something like this:
NSURL *myURL = [NSURL URLWithString: #"http://example.com/service.asmx"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: myURL];
[request setHTTPMethod: #"HEAD"];
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, NULL), ^{
NSURLResponse *response;
NSError *error;
NSData *myData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error];
BOOL reachable;
if (myData) {
// we are probably reachable, check the response
reachable=YES;
} else {
// we are probably not reachable, check the error:
reachable=NO;
}
// now call ourselves back on the main thread
dispatch_async( dispatch_get_main_queue(), ^{
[self setReachability: reachable];
});
});
If you want to check reachability against a URL (the one usually used is against a hostname) just do a HEAD request using a NSURLConnection.
Swift 5
A possible solution for Swift is:
func verifyURL(urlPath: String, completion: #escaping (_ isValid: Bool) ->()) {
if let url = URL(string: urlPath) {
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
let task = URLSession.shared.dataTask(with: request) { _, response, error in
if let httpResponse = response {
if httpResponse.getStatusCode() == 200 {
completion(true)
}
} else {
completion(false)
}
}
task.resume()
} else {
completion(false)
}
}
Then call the method like that:
verifyURL(urlPath: "www.google.com", completion: { (isValid) in
if isValid {
runYourCode()
} else {
print("URL: www.google.com is not reachable")
}
})