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];
Related
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.
Just a quick one how do i detect if a login failed here is my code:
- (IBAction)btnTimetable:(id)sender {
NSString *user = _txtUsername.text;
NSString *pass = _txtPassword.text;
NSString *content = [NSString stringWithFormat:#"username=%#&password=%#", user, pass];
NSData *data =[content dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postlenght=[NSString stringWithFormat:#"%lu",(unsigned long)[data length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://moodle.thomroth.ac.uk/login/index.php?mode=login"]];
[request setHTTPMethod:#"POST"];
[request setValue:postlenght forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:data];
//NSError *error=nil;
//NSURLResponse *response=nil;
//NSData *result=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
[_webView loadRequest:request];
[self performSelector:#selector(parseTimetable) withObject:nil afterDelay:3.0];
}
i dont even know where to start on this one is there a delegate method to detect such actions ?
As stated in the official Developer forums, UIWebView does not support authentication challenges in iOS. Please read here (requires developer account): UIWebView does not directly support authentication challenges
sendSynchronousRequest:returningResponse:error: should return nil and the status code of the returned response should equal 401 (Unauthorized):
[(NSHTTPURLRequest*)response statusCode] == 401
I would guess, the error parameter should be set to a corresponding error (please check this through printing it to the console).
If you use the delegate approach of NSURLConnection the situation is different:
When NSURLConnection receives a 401 (for example, the connection requires credentials for authentication, or a previous authentication attempt failed), it does invoke the delegate
- (void)connection:(NSURLConnection *)connection
willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
if implemented, otherwise it invokes these (now considered obsolete methods):
- (BOOL)connection:(NSURLConnection *)connection
canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
- (void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection;
if implemented.
The official documentation provided more information: NSURLConnectionDelegate Protocol Reference.
You can cancel the authentication request, if you decide to do so. As a result, the connection can fail.
If the connection fails, connection:didFailWithError will be invoked.
If you really want to do it using UIWebView you could use it's delegate method and parse code answer of your website.
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString *webSource = [_web stringByEvaluatingJavaScriptFromString:#"document.body.innerHTML"];
}
I came across NSURLConnection, I used it before, simply on request, and getting data and parsing it. However this time web developer has developed GET and POST requests.
I want through many tutorials and stack question and tried to get desired result.
As I see there is sometime request, like this
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"URL"]
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
[request setHTTPMethod: #"GET"];
NSError *requestError;
NSURLResponse *urlResponse = nil;
NSData *response1 = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];
also few others I have seen.
I looks easy but I am unable to find what is required for any POST and GET request.
The data which I have received from my web developer is
SOAP 1.2
POST /DEMOService/DEMO.asmx HTTP/1.1
Host: projects.demosite.com
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length
and in return there will be GET and POST
The following is a sample HTTP GET request and response. The placeholders shown need to be replaced with actual values.
GET /DEMOService/DEMO.asmx/VerifyLogin?username=string&password=string&AuthenticationKey=string HTTP/1.1
Host: projects.demosite.com
I am well-aware of delegates of NSURLConnections, which are following..
#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
}
THE ONLY THING WHERE I AM STUCK IS
How to write request where I have pass arguments, in GET or POST request.
Thanks
If your arguments are being sent in the URL itself (e.g., as part of the URL path or query string), then you just need to include them in the NSURL argument. For instance, you might have the following:
NSString *urlString = [NSString stringWithFormat:#"https://hostname/DEMOService/DEMO.asmx/VerifyLogin?username=%#&password=%#&AuthenticationKey=%#",
username,
password,
authenticationKey];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
where username, password, and authenticationKey are local variables you set elsewhere.
Your response from the server is stored by the data contained in the NSData instance returned by -[NSURLConnection sendSynchronousRequest:returningResponse:error:].
So in your example, your response above would be stored in the response1 variable. And you can convert this to a string and/or parse it as needed.
I currently have a screen with 2 tables. I'm getting the data synchronously and putting it on the screen. Code looks something like:
viewController.m
DBAccess_Error_T = [getList:a byCompanyID:1];
DBAccess_Error_T = [getList:b byCompanyID:2];
[self putListAOnScreen];
[self putListBOnScreen];
DBAccess.m
+ (DBAccess_Error_T)getList:(NSMutableArray*)a byCompanyID:(NSInteger)cID
{
// Pack this up in JSON form
[self queryDB:postData];
// Unpack and put it into variable a
}
+ (id)queryDB:(id)post
{
// Send request
// Get back data
}
I'm now trying to switch this over to async and I'm struggling. It's been hard even with website tutorials and documentations.
Since all of my database utilities are in separate files from the viewControllers, I'm not sure how I can use the didReceiveData and didReceiveResponse handlers. Also, since I have 2 arrays to fill for my 2 tables, how do I distinguish the difference in didReceiveData?
Instead, what I'm trying to do now is use sendAsynchronousRequest, but it seems I need to create an unpack function for every send function...let me know if I'm way off here...it looks something like:
viewController.m stays the same
DBAccess.m
+ (DBAccess_Error_T)getList:(NSMutableArray*)a byCompanyID:(NSInteger)cID
{
NSDictionary *post = /*blah blah*/
[self queryDB:post output:(a)];
}
+ (id)queryDB:(id)post output:(id)output
{
NSError *error;
NSData *jsonPayload = [NSJSONSerialization dataWithJSONObject:post options:NSJSONWritingPrettyPrinted error:&error];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody:jsonPayload];
[NSURLConnection sendAsynchronousRequest:request
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *error)
{
if ([data length] >0 && error == nil)
{
[self unpackDataForList:output data:data]; // This function needs to be different depending on which function called queryDB...the data will be unpacked in a different way
}
}
}
+ (void)unpackDataForList:(id)output data:(NSData*)data
{
// Do my unpacking here and stick it into 'output'.
}
How can I call a different unpackData function? are function pointers the right way to do this? Is this approach way off? Any tips would be greatly appreciated!
Have you ever looked at ASIHTTPRequest? It makes your life a lot easier by allowing you to use blocks. Here's an example of how to make an asynchronous request:
- (IBAction)grabURLInBackground:(id)sender
{
NSURL *url = [NSURL URLWithString:#"http://allseeing-i.com"];
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setCompletionBlock:^{
// Use when fetching text data
NSString *responseString = [request responseString];
// Use when fetching binary data
NSData *responseData = [request responseData];
}];
[request setFailedBlock:^{
NSError *error = [request error];
}];
[request startAsynchronous];
}
You can find more information here:
http://allseeing-i.com/ASIHTTPRequest/
This is the logical flow for my application:
At first, when the view controller has finished loading, then a NSURLConnection request can start its execution
The response consists in xml data
After parsing that xml I need to send another NSURLConnection request.
After sending the second request, if the response is ok, I receive other xml data
After parsing the second xml, I have to check some issues between first and second xml data.
So, is it possible to send multiple request? How? I do not need code, you could just explain it.
Thank you in advance.
I do this with the NSURLConnection Making them properties, then checking which one it is:
#property (nonatomic,retain) NSURLConnection *myConnection;
#property (nonatomic,retain) NSURLConnection *mySecondConnection;
then in the delegate:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
if (connection == myConnection){
//do something
}
if (connection == mySecondConnection){
// do something else
}
}
You can pass your NSURLRequest to the connection:
self.myConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
There is a third party library available which is a wrapper on CFNetwork is ASIHTTPREQUEST
This Library should do work for you. so that you don't have to write the code from scratch. other alternative is create one class which will be responsible for creating NSURLConnection then sending the request and finally notify to view controller using delegate or notification one data is received .
- (void)viedDidLoad{
[super viewDidLoad];
[self firstRequestMethod];
}
- (void)firstRequestMethod{
NSString *myFirstRequestURL = #"<URL>";
NSURL *webURL = [NSURL URLWithString:myFirstRequestURL];
NSURLRequest *request = [NSURLRequest requestWithURL:webURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSError *error;
NSURLResponse *response;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if(returnData)
{
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSASCIIStringEncoding];
//Parse your response here.
//Is desired response obtain call the second Request, as described above
if (TRUE) { //on success
[self secondRequestMethod];
}
}
}
- (void)secondRequestMethod{
NSString *mySecondRequestURL = #"<URL>";
NSURL *webURL = [NSURL URLWithString:mySecondRequestURL];
NSURLRequest *request = [NSURLRequest requestWithURL:webURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSError *error;
NSURLResponse *response;
NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if(returnData)
{
NSString *returnString = [[NSString alloc] initWithData:returnData encoding:NSASCIIStringEncoding];
//Parse your response here.
//Is desired response obtain call the second Request, as described above
if (TRUE) { //on success
//subsequent calls to other url, same as above
}
}
}
Hope this will help you understand better....