I'm building my first iOS app with a login system. I've built some simple things before, but this is on a whole new level. Currently I'm able to login and get some data back from the server. The app then performs a model segue to MainView with MainController attached to it.
When I log in, the app gets token and success back from the server. This token is needed for further requests to the server. However, I can't seem to figure out how to pass the token to MainController. It doesn't matter what I try, the app crashes with an unknown error.
Right now - with the code below - it throws:
2014-10-11 14:30:14.077 LoginScreen[29076:4688502] -[UINavigationController setXAuthToken:]: unrecognized selector sent to instance 0x7bfd1140
2014-10-11 14:30:14.104 LoginScreen[29076:4688502] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UINavigationController setXAuthToken:]: unrecognized selector sent to instance 0x7bfd1140'
ViewController.h
#import <UIKit/UIKit.h>
#import "MainController.h"
#interface ViewController : UIViewController <UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITextField *txtUsername;
#property (weak, nonatomic) IBOutlet UITextField *txtPassword;
- (IBAction)sigininClicked:(id)sender;
- (IBAction)backgroundTap:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)sigininClicked:(id)sender {
NSInteger success = 0;
#try {
if([[self.txtUsername text] isEqualToString:#""] || [[self.txtPassword text] isEqualToString:#""] ) {
[self alertStatus:#"Please enter Email and Password" :#"Sign in Failed!" :0];
} else {
NSString *post =[[NSString alloc] initWithFormat:#"username=%#&password=%#",[self.txtUsername text],[self.txtPassword text]];
NSLog(#"PostData: %#",post);
NSURL *url=[NSURL URLWithString:#"http://www.mywebsite.com/auth"];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
//[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];
NSError *error = [[NSError alloc] init];
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"Response code: %ld", (long)[response statusCode]);
if ([response statusCode] >= 200 && [response statusCode] < 300)
{
NSString *responseData = [[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
NSLog(#"Response ==> %#", responseData);
NSError *error = nil;
NSDictionary *jsonData = [NSJSONSerialization
JSONObjectWithData:urlData
options:NSJSONReadingMutableContainers
error:&error];
success = [jsonData[#"success"] integerValue];
NSLog(#"Success: %ld",(long)success);
if(success == 1)
{
NSLog(#"Login SUCCESS");
} else {
NSString *error_msg = (NSString *) jsonData[#"error_message"];
[self alertStatus:error_msg :#"Sign in Failed!" :0];
}
} else {
//if (error) NSLog(#"Error: %#", error);
[self alertStatus:#"Connection Failed" :#"Sign in Failed!" :0];
}
}
}
#catch (NSException * e) {
NSLog(#"Exception: %#", e);
[self alertStatus:#"Sign in Failed." :#"Error!" :0];
}
if (success) {
[self performSegueWithIdentifier:#"login_success" sender:self];
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"login_success"]) {
MainController *controller = (MainController *)segue.destinationViewController;
controller.xAuthToken = #"test string";
}
}
- (void) alertStatus:(NSString *)msg :(NSString *)title :(int) tag
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title
message:msg
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil, nil];
alertView.tag = tag;
[alertView show];
}
- (IBAction)backgroundTap:(id)sender {
[self.view endEditing:YES];
}
-(BOOL) textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
#end
MainController.h
#import <Foundation/Foundation.h>
#interface MainController : UIViewController
#property (nonatomic,strong) NSString *xAuthToken;
#end
MainController.m
#import "MainController.h"
#interface MainController ()
#end
#implementation MainController
- (void)viewDidLoad
{
NSLog(#"%#",_xAuthToken);
}
#end
The stack trace tells you what the problem is - you are trying to set the XAuthToken property on a UINavigationController - but a UINavigationController doesn't have that property.
Your MainController instance is embedded in a UINavigationController, so that is what you get from destinationViewController in prepareForSegue.
You need to access the view controller stack -
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"login_success"]) {
UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
MainController *controller = (MainController *)navController.topViewController
controller.xAuthToken = #"test string";
}
}
Related
My app is asking for permission to “Have offline access”, why? It's the weirdest thing. I've done a bit of searching and haven't really found anything that's worked. I've tried using these for scopes:
https://www.googleapis.com/auth/plus.profile.emails.read
https://www.googleapis.com/auth/plus.login
and that didn't seem to help.
Below is a screenshot and some of my code to help you see what's going on:
Some of my code:
#import "ViewController.h"
NSString *callbakc = #"http://localhost/";
NSString *client_id = #“CLIENT ID“;
NSString *scope = #"https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile+https://www.google.com/reader/api/0/subscription";
NSString *secret = #“SECRET”;
NSString *visibleactions = #"http://schemas.google.com/AddActivity";
#interface ViewController () {
NSString *authAccessToken;
UIAlertController *alertController;
}
#property (strong, nonatomic) NSMutableData *receivedData;
#property (weak, nonatomic) IBOutlet UIWebView *webView;
#end
#implementation ViewController
#pragma mark - Lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
NSString *url = [NSString stringWithFormat:#"https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=%#&redirect_uri=%#&scope=%#&data-requestvisibleactions=%#",client_id,callbakc,scope,visibleactions];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10];
[_webView loadRequest:request];
}
#pragma mark - WebView Delegate
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
[self performSelector:#selector(progressDelay:) withObject:nil afterDelay:0.0];
if ([[[request URL] host] isEqualToString:#"localhost"]) {
// Extract oauth_verifier from URL query
NSString* verifier = nil;
NSArray* urlParams = [[[request URL] query] componentsSeparatedByString:#"&"];
for (NSString* param in urlParams) {
if (![param isEqualToString:#"error=access_denied"]) {
NSArray* keyValue = [param componentsSeparatedByString:#"="];
NSString* key = [keyValue objectAtIndex:0];
if ([key isEqualToString:#"code"]) {
verifier = [keyValue objectAtIndex:1];
// NSLog(#"verifier %#",verifier);
break;
}
}
else {
[self.navigationController popViewControllerAnimated:NO];
}
}
if (!verifier==0) {
[self showAlertViewWithTitle:#"" message:#"Please wait" okAction:NO];
NSString *data = [NSString stringWithFormat:#"code=%#&client_id=%#&client_secret=%#&redirect_uri=%#&grant_type=authorization_code", verifier,client_id,secret,callbakc];
NSString *url = [NSString stringWithFormat:#"https://accounts.google.com/o/oauth2/token"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[data dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPShouldHandleCookies:NO];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
NSLog(#"Connection: %#", theConnection);
self.receivedData = [[NSMutableData alloc] init];
}
else {
// cancel button click
NSLog(#"not Verified!!");
}
return NO;
}
return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView {
// show progress
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[alertController dismissViewControllerAnimated:YES completion:nil];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
if (error.code==102) //Frame load interrupted
return;
[alertController dismissViewControllerAnimated:YES completion:nil];
[self showAlertViewWithTitle:#"Error" message:[error localizedDescription] okAction:YES];
}
#pragma mark - NSURLConnection Delegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
[self showAlertViewWithTitle:#"Error" message:[NSString stringWithFormat:#"%#", error] okAction:YES];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *response = [[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding];
NSData *data = [response dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *tokenData = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if ([tokenData objectForKey:#"access_token"]) {
authAccessToken = [tokenData objectForKey:#"access_token"];
[self getUserInfo:authAccessToken];
}
else {
[alertController dismissViewControllerAnimated:YES completion:nil];
NSLog(#"RESULT: %#", tokenData);
[self showAlertViewWithTitle:[tokenData objectForKey:#"name"] message:[NSString stringWithFormat:#"%#", tokenData] okAction:YES];
// Flush all cached data
[[NSURLCache sharedURLCache] removeAllCachedResponses];
}
}
#pragma mark - Private Method Implementation
-(void)getUserInfo:(NSString *)token {
NSString *url = [NSString stringWithFormat:#"https://www.googleapis.com/oauth2/v1/userinfo?access_token=%#",token];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"GET"];
[request setHTTPShouldHandleCookies:NO];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
NSLog(#"Connection: %#", theConnection);
self.receivedData = [[NSMutableData alloc] init];
}
-(void)progressDelay:(id)sender {
// Dismiss progress
}
#end
Any help would be greatly appreciated!
Thank you
This is from https://stackoverflow.com/questions/32210920/why-is-my-app-asking-for-permission-to-have-offline-access?answertab=oldest#tab-top:
This is normal behavior and occurs when the user has granted
permission already.
Basically, no need to worry about it unless you really don't want that
showing up, in that case, you need to un auth the users old token
before requesting a new one.
I'm not exactly sure how because I haven't done this before, but before you authorize a new token you need to un-authorize the old one.
You'll need to modify the -(void)getUserInfo:(NSString *)token method.
For some reason unknown to me. The email scope pops up with
Have offline access
If you want to remove the have offline access remove the email scope. Personally I think it is miss leading to users that you are asking for email access yet are prompted for offline access. Technically speaking all OAuth2 that returns a refresh token gives offline access so the user should always be told that you are getting offline access but it doesnt.
delegate method cannot callback
This is my .h file
#protocol ServiceAPIDelegate <NSObject>
#optional
- (void) onRequestLoginFinish:(NSDictionary*) dict;
#end
#interface ServiceAPI : NSObject
+ (id)shareAPI;
#property (nonatomic, weak) id <ServiceAPIDelegate> delegate_service;
;
#end
and this is .m file, i use ASIFORMData request and it is callback to requestFinished after get a response from server. but ater that, i want to send data to myviewcontroler use [self.delegate_service onRequestLoginFinish:result]; after this line. my program run normaly not bugs, not callback to function. I cannot see where errors are.
- (void) requestLoginWithUserName:(NSString*) username andPassWord:(NSString*) password {
NSString* urlString = [PublicInstance API_LOGIN];
NSArray *keys = PARAMS_ARRAY;
NSArray *objects = [NSArray arrayWithObjects:username, password, [#((int)En) stringValue], APPID, [PublicInstance getDevideID], DEVIDEOS, nil];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjects:objects forKeys:keys];
NSString* signData = [PublicInstance signData:dict];
[dict setObject:signData forKey:signKey];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:urlString]];
[request addRequestHeader:#"Content-Type" value:#"application/json"];
[request setValidatesSecureCertificate:NO];
[request setRequestMethod:#"POST"];
NSData *jsonDataToPost = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil];
[request appendPostData:jsonDataToPost];
[request startAsynchronous];
[request setDelegate:self];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSError* error = [request error];
if(!error) {
NSString* responseString = [request responseString];
NSDictionary *result = [NSJSONSerialization JSONObjectWithData;
[self.delegate_service onRequestLoginFinish:result];
}else{
NSLog(#"%#", [error description]);
}
}
ServiceAPI *ShareServiceAPI;
- (id) init {
if ([self init]) {
ShareServiceAPI = [ServiceAPI shareAPI];
ShareServiceAPI.delegate_service =self;
}
}
- (void) requestLoginWithUserInfor:(UserInfor*) _userinfor {
[ShareServiceAPI requestLoginWithUserName:_userinfor.username andPassWord:_userinfor.password];
}
#####################
and this is delegate method - but never callback (O^-oO)
- (void) onRequestLoginFinish:(NSDictionary *)dict {
if ([[dict objectForKey:Key_Status] intValue] == 1) {
NSLog(#"login successful");
}
else {
NSLog(#"login fail....");
}
}
Could anyone please help me?. Thank you for your time
You should always check if the delegate is nil or not and also check if the delegate responds to the selector as :
if(self.delegate_service){
if([self.delegate_service repondsToSelector:#selector(onRequestLoginFinish:)]){
[self.delegate_service onRequestLoginFinish:result];
}
}
Through the way you can make your program safe and found the reason that the method is not called.
Hi I am new to IOS development. I am developing an app that pulls data from a webservice. It has login and logout session and a whole lot of JSON API calls that i intent to implement using RestKit.
Now the issue is my login is working and gets success code 200 but fails to go to the next View Controller Scene because. I don't know how to get the sessionId.
Here is my code and JSON ;
{
"details": {
"username": "MY USER NAME",
"password": "MY MD5 CONVERTED PASS" }
}
Expected Returned JSON syntax (Success)
{
"response": {
"code": 200,
"resp_code": "USER_SESSION_LOGGED_IN", "sid": "as4ads68ds468486essf879g8de9sdg", "session_info": {
"details": {
"firstname" : "MY NAME",
"email_address" : "MY EMAIL", "company_id" : 1,
"user_id" : 1,
"surname" : "MY SURNAME",
"cell_number" : "MY NUMBER",
"username" : "MY USERNAME"
} }
} }
//
// ViewController.m
//
// Created by Cockpit Alien on 2014/10/27.
// Copyright (c) 2014 CockpitAlien. All rights reserved.
//
#import "ViewController.h"
#import <CommonCrypto/CommonDigest.h>
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*+ (NSString*)md5HexDigest:(NSString*)input
{
const char* str = [input UTF8String];
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, strlen(str), result);
NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2];
for(int i = 0; i<CC_MD5_DIGEST_LENGTH; i++) {
[ret appendFormat:#"%02x",result[i]];
}
return ret;
}*/
- (NSString *) md5:(NSString *) input
{
const char *cStr = [input UTF8String];
unsigned char digest[16];
CC_MD5( cStr, strlen(cStr), digest ); // This is the md5 call
NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
[output appendFormat:#"%02x", digest[i]];
return output;
}
- (IBAction)signinClicked:(id)sender {
NSInteger success = 0;
NSString *username = self.txtUsername.text;
NSString *password = self.txtPassword.text;
NSString *md5Password = [self md5:password];
#try {
if([username isEqualToString:#""] || [password isEqualToString:#""] ) {
[self alertStatus:#"Please enter Email and Password" :#"Sign in Failed!" :0];
} else {
NSString *post = [[NSString alloc] initWithFormat:#"username=%#&password=%#",username,md5Password];
NSLog(#"PostData: %#",post);
// Create the request
NSURL *url=[NSURL URLWithString:#"MY JSON HTTP/S URL HERE"];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%lu",(unsigned long)[postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
NSLog(#"Request Mutable, %#", request);
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
//[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];
NSError *error = [[NSError alloc] init];
NSHTTPURLResponse *response = nil;
// Create url connetion and fire requests
NSData *urlData=[NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
NSLog(#"Response code: %ld", (long)[response statusCode]);
if ([response statusCode] >= 200 && [response statusCode] < 300)
{
NSString *responseData = [[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
NSLog(#"Response ==> %#", responseData);
NSError *error = nil;
NSDictionary *jsonData = [NSJSONSerialization
JSONObjectWithData:urlData
options:NSJSONReadingMutableContainers
error:&error];
success = [jsonData[#"success"] integerValue];
NSLog(#"Success: %ld",(long)success);
if(success == 1)
{
NSLog(#"Login SUCCESS");
} else {
NSString *error_msg = (NSString *) jsonData[#"error_message"];
[self alertStatus:error_msg :#"Sign in Failed!" :0];
NSLog(#"Loging failed");
}
} else {
//if (error) NSLog(#"Error: %#", error);
[self alertStatus:#"Connection Failed" :#"Sign in Failed!" :0];
}
}
}
#catch (NSException * e) {
NSLog(#"Exception: %#", e);
[self alertStatus:#"Sign in Failed." :#"Error!" :0];
}
if (success) {
[self performSegueWithIdentifier:#"login_success" sender:self];
}
}
- (void) alertStatus:(NSString *)msg :(NSString *)title :(int) tag
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title
message:msg
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil, nil];
alertView.tag = tag;
[alertView show];
}
#end
//
// ViewController.h
//
// Created by CockPit on 2014/10/27.
// Copyright (c) 2014 CockpitAliens. All rights reserved.
//
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UITextField *txtUsername;
#property (weak, nonatomic) IBOutlet UITextField *txtPassword;
- (IBAction)signinClicked:(id)sender;
#end
Here is my storyboard. I want to be able to login and go to the next view controller. because I am going to also have to logout the sessionId parsing this JSON.
In yoursigninClicked method,
- (IBAction)signinClicked:(id)sender
{
....
....
if ([response statusCode] >= 200 && [response statusCode] < 300)
{
....
....
if(success == 1)
{
NSLog(#"Login SUCCESS");
}
....
....
}
....
....
}
So you are doing nothing else than printing NSLog. You are checking statusCode is successful or not and in that checking success variable value. In that if condition you should require executable code or method call, which will push application for further execution.
I'm trying to return String from this method i have two class
first one is for UI and it have two input text user and pass and also i have submit button , another one only doing the following method .
I'm trying to return string from the other class to this class and show the string in alert .
#import "LoginPage.h"
#implementation LoginPage
-(NSString *)responsData:(NSString *)loginUrl input1:(NSString *)username input2:(NSString *)password
{
NSString *urlAsString = loginUrl;
NSString*test;
inUsername = username;
inPassword = password;
NSURL *url = [NSURL URLWithString:urlAsString];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
[urlRequest setTimeoutInterval:30.0f];
[urlRequest setHTTPMethod:#"POST"];
// Setting Username and password
NSString *body = [NSString stringWithFormat:#"sended=yes&username=%#&password=%#",username,password];
[urlRequest setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection
sendAsynchronousRequest:urlRequest
queue:queue
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *error)
{
if ([data length] >0 && error == nil){ NSString *html =
[[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
// NSLog(#"%#", html);
self.lastValue = [NSString stringWithFormat:#"%#",html];
}
else if ([data length] == 0 && error == nil){
//NSLog(#"Nothing was downloaded.");
self.lastValue = [NSString stringWithFormat:#"No thing was downloaded"];
}
else if (error != nil){
// NSLog(#"Error happened = %#", error);
self.lastValue = [NSString stringWithFormat:#"%#",error];
} }];
NSLog(#"%#",self.lastValue);
return self.lastValue;
}
// Do any additional setup after loading the view, typically from a nib.
#end
i want to use this function in another view ( already i include the header of this file ) but i can't , can any one solve this >
another view
- (IBAction)submit:(id)sender {
LoginPage * login = [[LoginPage alloc]init];
NSString * dataRe;
dataRe = [login responsData:#"http://fahads-macbook-pro.local/ios/post.php" input1:#"admin" input2:#"1234"];
NSLog(#"%#",login.lastValue);
if (dataRe != nil) {
UIAlertView * alert =[[UIAlertView alloc]
initWithTitle:#"Hello Fahad"
message:[NSString stringWithFormat:#"%#",dataRe] delegate:self cancelButtonTitle:#"Okay ! " otherButtonTitles:nil, nil];
[alert show];
}
}
Thank you again
When you call the function on the other view, it send an asynch request to the web.
So when you do:
return self.lastValue;
lastValue is still empty or with the previous value because the competionHandler need still to be called. Code of the completionHandler, is just a peace of code passed to the function, that will be called at right moment. So the function arrive to the end where is your return.
When instead the completion handler block is called (because the request has produced a response), you assign the value:
self.lastValue = [NSString stringWithFormat:#"%#",html];
Now lastValue is right.
So your function shouldn't return an NSString, but should return void.
To pass the string to the other controller, you should use the delegation pattern.
This is a very quickly example
SecondViewController.h
#protocol SecondViewControllerDelegate <NSObject>
- (void)lastValueDidUpdate:(NSString *)lastValue;
#end
#interface SecondViewController : UIViewController
#property (weak, nonatomic) id<SecondViewControllerDelegate>delegate;
#end
SecondViewController.m
#import "SecondViewController.h"
#implementation SecondViewController
-(NSString *)responsData:(NSString *)loginUrl input1:(NSString *)username input2:(NSString *)password
{
NSString *urlAsString = loginUrl;
NSString*test;
inUsername = username;
inPassword = password;
NSURL *url = [NSURL URLWithString:urlAsString];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
[urlRequest setTimeoutInterval:30.0f];
[urlRequest setHTTPMethod:#"POST"];
// Setting Username and password
NSString *body = [NSString stringWithFormat:#"sended=yes&username=%#&password=%#",username,password];
[urlRequest setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
__weak typeof (self) weakSelf = self;
[NSURLConnection
sendAsynchronousRequest:urlRequest
queue:queue
completionHandler:^(NSURLResponse *response,
NSData *data,
NSError *error)
{
if ([data length] >0 && error == nil){ NSString *html =
[[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
// NSLog(#"%#", html);
weakSelf.lastValue = [NSString stringWithFormat:#"%#",html];
}
else if ([data length] == 0 && error == nil){
//NSLog(#"Nothing was downloaded.");
weakSelf.lastValue = [NSString stringWithFormat:#"No thing was downloaded"];
}
else if (error != nil){
// NSLog(#"Error happened = %#", error);
weakSelf.lastValue = [NSString stringWithFormat:#"%#",error];
}
[weakSelf.delegate lastValueDidUpdate:weakSelf.lastValue];
}];
}
#end
FirstViewController.h
#import "SecondViewController.h"
#interface FirstViewController : UIViewController <SecondViewControllerDelegate>
#property (strong, nonatomic) SecondViewController *secondViewController;
#end
FirstViewController.m
#import "FirstViewController.h"
#implementation FirstViewController
- (void)viewDidLoad {
//your code
[_secondViewController setDelegate:self];
//your code
}
//your code
#end
Note that i use a weak reference to self because otherwise you can create retain cycle.
Define a method in your first class i.e. UI class like:
- (void)callFromBlock:(NSString*)stringFromResponse
{
if (stringFromResponse != nil) {
UIAlertView * alert =[[UIAlertView alloc]
initWithTitle:#"Hello Fahad"
message:[NSString stringWithFormat:#"%#",stringFromResponse] delegate:self cancelButtonTitle:#"Okay ! " otherButtonTitles:nil, nil];
[alert show];
}
}
and the submit method should look like:
- (IBAction)submit:(id)sender {
LoginPage * login = [[LoginPage alloc]init];
NSString * dataRe;
dataRe = [login responsData:#"http://fahads-macbook-pro.local/ios/post.php" input1:#"admin" input2:#"1234"];
}
Now instead of return statement in the block, call the callFromBlock method from the block when you get the response and pass the string to this method you were trying to return.
Hope it helps.
I'm posting to a RESTful webservice and receiving a response, this works great if I'm getting back only a few records however there is a threshold where didReceiveData: stops being called (6 records) and it just hangs. (does not matter what records, just the number)
I can't seem to figure out why. I'm getting a status message of 200 application/json in didReceiveResponse: however that's the last I hear from my connection.
From other clients I can get the full data with any number of records so it's related to my NSURLConnection code.
See full NSURLConnection Post class below.
the .h
#import <Foundation/Foundation.h>
#import "MBProgressHUD.h"
#protocol PostJsonDelegate <NSObject, NSURLConnectionDelegate>
#optional
- (void) downloadFinished;
- (void) downloadReceivedData;
- (void) dataDownloadFailed: (NSString *) reason;
#end
#interface PostURLJson : NSObject {
NSMutableData *receivedData;
int expectedLength;
MBProgressHUD *HUD;
}
#property (strong, nonatomic) NSMutableData *receivedData;
#property (weak) id <PostJsonDelegate> delegate;
#property (assign, nonatomic) int expectedLength;
+ (id)initWithURL:(NSString *)url dictionary:(NSDictionary *)dictionary withDelegate:(id <PostJsonDelegate>)delegate;
#end
the .m
#import "PostURLJson.h"
#define SAFE_PERFORM_WITH_ARG(THE_OBJECT, THE_SELECTOR, THE_ARG) (([THE_OBJECT respondsToSelector:THE_SELECTOR]) ? [THE_OBJECT performSelector:THE_SELECTOR withObject:THE_ARG] : nil)
#implementation PostURLJson
#synthesize delegate;
#synthesize receivedData;
#synthesize expectedLength;
+ (id)initWithURL:(NSString *)url dictionary:(NSDictionary *)dictionary withDelegate:(id <PostJsonDelegate>)delegate
{
if (!url)
{
NSLog(#"Error. No URL");
return nil;
}
PostURLJson *postJson = [[self alloc] init];
postJson.delegate = delegate;
[postJson loadWithURL:url dictionary:dictionary];
return postJson;
}
- (void)loadWithURL:(NSString *)url dictionary:(NSDictionary *)dictionary
{
[self setExpectedLength:0];
receivedData = [[NSMutableData alloc] init];
NSError* error;
NSDictionary *tmp = [[NSDictionary alloc] initWithDictionary:dictionary];
NSData *postdata = [NSJSONSerialization dataWithJSONObject:tmp options:0 error:&error];
NSString *someString = [[NSString alloc] initWithData:postdata encoding:NSASCIIStringEncoding];
NSLog(#"%#",someString);
NSString *postLength = [NSString stringWithFormat:#"%d", [postdata length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:url]];
[request setHTTPMethod:#"POST"];
[request setTimeoutInterval:10.0f];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postdata];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
[connection start];
[self setLoadingModeEnabled:YES];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *) response;
int errorCode = httpResponse.statusCode;
NSString *fileMIMEType = [[httpResponse MIMEType] lowercaseString];
NSLog(#"%d",errorCode);
NSLog(#"%#",fileMIMEType);
[receivedData setLength:0];
// Check for bad connection
expectedLength = [response expectedContentLength];
if (expectedLength == NSURLResponseUnknownLength)
{
NSString *reason = [NSString stringWithFormat:#"Invalid URL"];
SAFE_PERFORM_WITH_ARG(delegate, #selector(dataDownloadFailed:), reason);
[connection cancel];
return;
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[receivedData appendData:data];
SAFE_PERFORM_WITH_ARG(delegate, #selector(downloadReceivedData), nil);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
SAFE_PERFORM_WITH_ARG(delegate, #selector(downloadFinished), nil);
[self setLoadingModeEnabled:NO];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"Something went wrong...");
HUD.labelText = #"Something went wrong...";
[self performSelector:#selector(didFailHideHud) withObject:nil afterDelay:2];
}
- (void)setLoadingModeEnabled:(BOOL)isLoading
{
//when network action, toggle network indicator and activity indicator
if (isLoading) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
UIWindow *window = [UIApplication sharedApplication].keyWindow;
HUD = [[MBProgressHUD alloc] initWithWindow:window];
[window addSubview:HUD];
HUD.labelText = #"Loading";
[HUD show:YES];
} else {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[HUD hide:YES];
[HUD removeFromSuperview];
}
}
-(void)didFailHideHud
{
[HUD hide:YES];
[HUD removeFromSuperview];
}
#end
Edit Server was not giving back a valid length after a certain size triggering NSURLResponseUnknownLength which I had mistakenly not logged so I was not getting my "Invalid URL" message in the console.
if (expectedLength == NSURLResponseUnknownLength)
{
NSString *reason = [NSString stringWithFormat:#"Invalid URL"];
SAFE_PERFORM_WITH_ARG(delegate, #selector(dataDownloadFailed:), reason);
[connection cancel];
return;
}
Try this:
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[connection start];
Because if you run the connection in the NSDefaultRunLoopMode and there is UITableView scrolling, it will hang the connection delegate.
However your problem is different. Some servers will limit the number of simultaneous connections from a single client. If you are in the case, then the first connections would succeed and the others would hang until previous connections complete.