I'm currently working on an app that uses a basic login page to check if the user has access to the app. I've made the login part with help of this tutorial which uses this frame work to use username and password from a simple web script.
I'm hoping that someone else maybe have worked with it or can help me with my issue. I wan't to show an activity indicator, I'm using MBProgressHUD as an activity indicator.
So I've experimented with it but can't get it to show the activity indicator when the app is connecting to the URL. I've done some bad connection simulating with the login process but the activity indicator won't show up when the app is connecting to the URL. It only shows on errors and the only thing that shows any kind of loading on success is that the login buttons pressed state is "active" (blue highlighted) until the loading is done.
So here's my code that runs when the user has typed in username and password and clicks on the login button:
// Login button
- (IBAction)loginBtnClicked:(id)sender
{
// Show the activity indicator
[HUD showUIBlockingIndicatorWithText:#"Loggar in..."];
#try {
if([[userNameTxtField text] isEqualToString:#""] || [[passwordTxtField text] isEqualToString:#""] ) {
// No username or password entered
[self alertStatus:#"Du måste ange användarnamn och lösenord" :#"Något gick fel!"];
// Hide activity indicator
[HUD hideUIBlockingIndicator];
} else {
NSString *post =[[NSString alloc] initWithFormat:#"username=%#&password=%#",[userNameTxtField text],[passwordTxtField text]];
NSLog(#"PostData: %#",post);
NSURL *url=[NSURL URLWithString:#"http://www.nebulon.se/json/sendus/jsonlogin.php"];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [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: %d", [response statusCode]);
if ([response statusCode] >=200 && [response statusCode] <300){
NSString *responseData = [[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
NSLog(#"Response ==> %#", responseData);
SBJsonParser *jsonParser = [SBJsonParser new];
NSDictionary *jsonData = (NSDictionary *) [jsonParser objectWithString:responseData error:nil];
NSLog(#"%#",jsonData);
NSInteger success = [(NSNumber *) [jsonData objectForKey:#"success"] integerValue];
NSLog(#"%d",success);
if(success == 1){
// Login success, grant user access to app
NSLog(#"Login SUCCESS");
[self loginSuccess];
// Hide activity indicator
[HUD hideUIBlockingIndicator];
// Store username
NSString *userName = [userNameTxtField text];
NSUserDefaults *UserDefaults = [NSUserDefaults standardUserDefaults];
[UserDefaults setObject:userName forKey:#"userName"];
[UserDefaults synchronize];
[self dismissViewControllerAnimated:NO completion:nil];
} else {
// Login error
NSString *error_msg = (NSString *) [jsonData objectForKey:#"error_message"];
[self alertStatus:error_msg :#"Inloggningen misslyckades"];
[self loginFailed];
// Hide activity indicator
[HUD hideUIBlockingIndicator];
}
} else {
// Login error
if (error) NSLog(#"Error: %#", error);
[self alertStatus:#"Ingen nätverksuppkoppling hittades." :#"Ett fel har inträffat!"];
[self loginFailed];
// Hide activity indicator
[HUD hideUIBlockingIndicator];
}
}
}
#catch (NSException * e) {
// Login error
NSLog(#"Exception: %#", e);
[self alertStatus:#"Inloggningen misslyckades." :#"Ett fel har inträffat!"];
[self loginFailed];
// Hide activity indicator
[HUD hideUIBlockingIndicator];
}
}
I believe the issue is due to the use of sendSynchronousRequest:returningResponse:error:
This method will be blocking the main/UI thread so the HUD never actually gets a chance to show until the method has returned, at which point the code continues and the HUD is hidden.
I think you should be looking at using an asynchronous request. and implementing the NSURLConnection delegate methods.
EDIT: Added code sample.
Assuming you're targeting iOS 5 and higher you can use the following code snippet which takes advantage of blocks with sendAsynchronousRequest:queue:completionHandler: and GCD.
NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request
queue:backgroundQueue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// NOTE: This block is called on the background queue.
// Use GCD to get back onto the main thread
dispatch_async(dispatch_get_main_queue(), ^{
// This block will process the response and data on the main thread
});
}];
It really is very little work to port your existing code to use this mechanism. If you don't know how blocks work you should read the documentation as they are a very powerful language feature and are being used in an increasing amount of Apple and third-party frameworks.
I would also recommend staying AWAY from third-party networking libraries for now until you understand the nuances that can cause issues such as this.
Related
I believe that I have an incorrect flow for implementing Developer Authenticated Identities and I keep hearing and doing different things on the web. So I thought I'd present my entire flow and hear what the correct way of doing this was and present some questions and errors at the bottom.
Initially, I have a user login with a password and username (I'm using nsuserdefaults only temporarily, I will use KeyChain later).
Note: I also have a callback that goes all the way down to see if I properly authenticated a user.
Login Method:
-(void)loginBusyTimeUser:(void(^)(BOOL))callBack{
//initialize nsuserdefualts should be keychain later
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *post = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
[defaults objectForKey:#"username"], #"username",
[defaults objectForKey:#"password"], #"password",
nil];
NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject:post options:0 error:&error];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:#"SOMELOGINURL"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:postData];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *newJSON = [NSJSONSerialization JSONObjectWithData:data
options:0
error:&error];
if(!newJSON || [newJSON objectForKey:#"errorMessage"]){
NSLog(#"%#",newJSON);
callBack(false);
NSLog(#"DID NOT AUTHENTICATE");
}else{
NSLog(#"%#",newJSON);
[defaults setValue:[newJSON objectForKey:#"Token"] forKey:#"Token"];
[defaults setValue:[newJSON objectForKey:#"IdentityId"] forKey:#"IdentityId"];
[self authenticateUser:^(BOOL call){
callBack(call);
}];
}
}] resume];
}
If Everything is successful, I then authenticate my user with AWS Cognito with the developer authenticated flow:
-(void)authenticateUser:(void(^)(BOOL))callBack{
//Now after making sure that your user's credentials are sound, then initialize the IdentityProvider, in this case
//BusytimeAuthenticated
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
id<AWSCognitoIdentityProvider> identityProvider = [[BusytimeAuthenticated alloc] initWithRegionType:AWSRegionUSEast1
identityId:nil
identityPoolId:#"SOMEPOOLID"
logins:#{#"cognito-identity.amazonaws.com": [defaults objectForKey:#"Token"]}
providerName:#"cognito-identity.amazonaws.com"
];
credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1
identityProvider:identityProvider
unauthRoleArn:nil
authRoleArn:nil];
configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1
credentialsProvider:self.credentialsProvider];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
[[credentialsProvider refresh] continueWithBlock:^id(AWSTask *task){
if([task isFaulted]){
callBack(false);
}else{
callBack(true);
}
return [defaults objectForKey:#"Token"];
}];
}
Then the refresh method causes some errors so I'll show my "BusytimeAuthenticated" class (.m)
//
// BusytimeAuthenticated.m
// BusyTime
//
// Created by akash kakumani on 10/14/15.
// Copyright (c) 2015 BusyTime. All rights reserved.
//
#import "BusytimeAuthenticated.h"
#interface BusytimeAuthenticated()
#property (strong, atomic) NSString *providerName;
#property (strong, atomic) NSString *token;
#end
#implementation BusytimeAuthenticated
#synthesize providerName=_providerName;
#synthesize token=_token;
- (instancetype)initWithRegionType:(AWSRegionType)regionType
identityId:(NSString *)identityId
identityPoolId:(NSString *)identityPoolId
logins:(NSDictionary *)logins
providerName:(NSString *)providerName{
if (self = [super initWithRegionType:regionType identityId:identityId accountId:nil identityPoolId:identityPoolId logins:logins]) {
self.providerName = providerName;
}
return self;
}
// Return the developer provider name which you choose while setting up the
// identity pool in the Amazon Cognito Console
- (BOOL)authenticatedWithProvider {
return [self.logins objectForKey:self.providerName] != nil;
}
// If the app has a valid identityId return it, otherwise get a valid
// identityId from your backend.
- (AWSTask *)getIdentityId {
// already cached the identity id, return it
if (self.identityId) {
return [AWSTask taskWithResult:nil];
}
// not authenticated with our developer provider
else if (![self authenticatedWithProvider]) {
return [super getIdentityId];
}
// authenticated with our developer provider, use refresh logic to get id/token pair
else {
return [[AWSTask taskWithResult:nil] continueWithBlock:^id(AWSTask *task) {
if (!self.identityId) {
return [self refresh];
}
return [AWSTask taskWithResult:self.identityId];
}];
}
}
// Use the refresh method to communicate with your backend to get an
// identityId and token.
- (AWSTask *)refresh {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (![self authenticatedWithProvider]) {
return [super getIdentityId];
}else{
NSDictionary *post = [[NSDictionary alloc] initWithObjectsAndKeys:
[defaults objectForKey:#"username"], #"username",
[defaults objectForKey:#"password"], #"password",
nil];
NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject:post options:0 error:&error];
NSString *postLength = [NSString stringWithFormat:#"%lu", (unsigned long)[postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:#"SOMELOGINURL"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:postData];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSDictionary *newJSON = [NSJSONSerialization JSONObjectWithData:data
options:0
error:&error];
if(!newJSON){
NSLog(#"Failure in refresh: %#",newJSON);
}else{
NSLog(#"The IdentityID in the refresh method: %#",[newJSON objectForKey:#"IdentityId" ]);
NSLog(#"The token in the refresh method: %#",[newJSON objectForKey:#"Token" ]);
self.identityId = [newJSON objectForKey:#"IdentityId" ];
self.token = [newJSON objectForKey:#"Token" ];
}
}] resume];
return [AWSTask taskWithResult:self.identityId];
}
return [AWSTask taskWithResult:self.identityId];
}
#end
Some Questions I had:
Is the DeveloperAuthenticationClient necessary to solve my problems? I saw the sample app using them but I found them too confusing.
Should I be using my ProviderName or should I be using "cognito-identity.amazonaws.com"
I sometimes face a timeout error and found out that it could be the fact that my implementation of login (using API Gateway and a lambda method) could have some cold-start issues. The way I solved this is by increasing the timeout time to 20 seconds. Is this the correct way to solve this?
I saw that in the sample app they use GetToken and Login as two separate things. I thought it would be easier if my login could also serve as my GetToken. Is this appropriate?
Finally, please address any problems that you see with my code if time permits.
Error:
[Error] AWSCredentialsProvider.m line:527 |
__40-[AWSCognitoCredentialsProvider refresh]_block_invoke352 | Unable to refresh. Error is [Error
Domain=com.amazonaws.AWSCognitoCredentialsProviderErrorDomain Code=1
"identityId shouldn't be nil"
UserInfo={NSLocalizedDescription=identityId shouldn't be nil}]
(I also found out the error above is related to the fact that self.identityId didn't get set because the request was in a block and other parts executed first and the solution is:
- (AWSTask *)refresh {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (![self authenticatedWithProvider]) {
return [super getIdentityId];
}else{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *string = [defaults objectForKey:#"IdentityId"];
self.identityId = string;
return [AWSTask taskWithResult:[defaults objectForKey:#"IdentityId"]];
}
NSString *string = [defaults objectForKey:#"IdentityId"];
return [AWSTask taskWithResult:[defaults objectForKey:#"IdentityId"]];
}
But I believe that this isn't the correct implementation. )
I believe my code was working at a certain point but stopped working after I upgraded to the new SDK. However, it might just be the fact that I hadn't noticed the error initially.
Answers to your questions:
Yes you need to have some entity(client) which communicates with your backend system.
You are using cognito-identity.amazonaws.com in the logins map, but using the IdentityProvider pattern for refresh. This is why the first authentication succeeds but attempts to refresh fail. The logic in your refresh will never fire. Please look at our end to end sample on how to implement developer authenticated identities.
From my limited knowledge yes this is one way, but you may face performance issues. Please contact AWS Lambda via their forums for more guidance on this.
We highly recommend following the flow in the sample. The getToken doesn't need your authentication credentials if you have established trust, whereas login will always need the authentication credentials, so better not to mix these.
Thanks..
I am using NSURLConnection sendAsynchronousRequest to fetch some data from the web server. The issue is sometimes I am able to fetch the data and some times NULL is returned from the web server. When I looked in to the server logs, it has sent the data. I suspect it as a timing issue. Below is my code.
if(netStatus==ReachableViaWiFi || netStatus==ReachableViaWWAN)
{
NSLog(#"YES WIFI");
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[NSURL URLWithString:formattedURL];
[request setURL:[NSURL URLWithString:formattedURL]];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *jsonData, NSError *error)
{
progressbar.progress = 0.0;
NSLog(#"Next line of code");
NSLog(#"dataAsString %#", [NSString stringWithUTF8String:[jsonData bytes]]);
if ([NSString stringWithUTF8String:[jsonData bytes]]==NULL)
{
NSLog(#"R U HERE...THE RESULT IS NULL");
UIStoryboard *storybord = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *viewController = [storybord instantiateViewControllerWithIdentifier:#"noserver"];
viewController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:viewController animated:YES];
}
else
{
searchTable.hidden=FALSE;
progressbar.hidden=TRUE;
NSLog(#"R U HERE...THE RESULT IS NOT NULL");
NSError *error1;
dictresult = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error1];
self.searchTable.dataSource=self;
self.searchTable.delegate=self;
[searchTable reloadData];
}
NSLog(#"Asynch request in search results finished!");
if (error) {
} else {
}
}];
}
Is there a default timeout which is set when expecting data from the web server. Can I increase the default timeout or can I instruct to wait for some more time till I receive the data from the server. Please let me know how to accomplish this task. Thanks for your time
I having a situation here where I have some templates (objects), which I want to push to my backend. Now When the users presses the synchronise button, there is a connection to the backend form which I get all the templates. The templates on the IPAD app are compared to those that were transferred from the backend. If a template on the IPAD app has the ID(0), then there is a post to the backend. the backend then returns the saved template with his ID (to add to the local stored template).
Now I think my problem here is that I'm using the same connection (templateupdateconnection => bad name...) for all of those posts to the backend. The actual problem is that in the connectiondidfinishloading method, I only get a response on the last template that was posted to the backend.
Anyone who knows how I can solve this?
Thanks in advance!!
THE CODE
-(void)syncRegistrations:(NSArray *)arrayOfRegistrations{
NSFetchRequest *request = [[NSFetchRequest alloc]init];
[request setEntity:[NSEntityDescription entityForName:#"Registration" inManagedObjectContext:self.managedObjectContext]];
NSError *error;
NSArray *Data = [self.managedObjectContext executeFetchRequest:request error:&error];
BOOL old=FALSE;
for(int registrationCounter = 0; registrationCounter < arrayOfRegistrations.count; registrationCounter ++){
NSDictionary *dictRegistration = [arrayOfRegistrations objectAtIndex:registrationCounter];
for(Registration *registration in Data){
if([dictRegistration objectForKey:#"id"] == registration.id){
old = TRUE;
}
else if ([registration.id intValue]==0){
NSString *jsonRequest = [NSString stringWithFormat:#"{\"form\":%#}",registration.form.id];
NSLog(#" de jsonrequest: %#",jsonRequest);
NSURL *url = [NSURL URLWithString:#"http://mybackend/registrations"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
NSData *requestData = [NSData dataWithBytes:[jsonRequest UTF8String] length:[jsonRequest length]];
[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];
self.registrationtoupdate = registration;
self.registrationUpdateConnection = NULL;
self.registrationUpdateConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[self.registrationUpdateConnection start];
registration.id=[NSNumber numberWithInteger:-1];
[self.managedObjectContext save:&error];
old=TRUE;
}
}
if(old==FALSE){
//nieuwe template toevoegen
Registration *registration = [NSEntityDescription insertNewObjectForEntityForName:#"Registration" inManagedObjectContext:self.managedObjectContext];
registration.id = [dictRegistration objectForKey:#"id"];
registration.form = [self getFormByID:[dictRegistration objectForKey:#"form"]];
[self.managedObjectContext save:&error];
}
old=FALSE;
}
//[self getRC];
[self performSelector:#selector(getRC) withObject:nil afterDelay:3];
}
CONNECTIONDIDFINISHLOADING METHOD
else if([connection isEqual:self.registrationUpdateConnection]){
NSDictionary *dictRegistration = [NSJSONSerialization JSONObjectWithData:self.registrationdata options:kNilOptions error:&error];
NSLog(#"de data van de registratie is: %#",dictRegistration);
NSLog(#"de registration to update is: %#",self.registrationtoupdate);
self.registrationtoupdate.id = [dictRegistration objectForKey:#"id"];
[self.managedObjectContext save:&error];
}
You should encapsulate the NSURLConnection and all relevant state data into a class. Let that class have a start method which asynchronously starts the connection and a completion block which has a parameter result representing the eventual result of the request. That way, you don't mismatch response data with other connections.
Then, in the completion block, reference the corresponding context (registration to update), get the JSON representation and update the context on the correct thread or queue appropriately for the given managed object context (see below "Thread Confinement"):
So, basically:
else if ([registration.id intValue]==0) {
// setup the request
NSMutableURLRequest *request = ...
MyHTTPRequestOperation* op =
[[MyHTTPRequestOperation alloc] initWithRequest:request
completion:^(void)(id result)
{
if (![result isKindOfClass:[NSError class]]) {
assert(result != nil && [result isKindOfClass:[NSData class]]);
NSDictionary* dictRegistration =
[NSJSONSerializationJSONObjectWithData:result
options:kNilOptions
error:&error];
// Todo: check if dictRegistration has the required type
id newRegistrationID = [dictRegistration objectForKey:#"id"];
// Update the model on the thread/queue you defined for the
// NSManagedObjectContext:
[self updateRegistration:registration withID: newRegistrationID];
}
else {
// an error occurred
}
}];
[op start];
registration.id=[NSNumber numberWithInteger:-1];
}
You should be familiar with NSModelObjectContext and "Thread Confinement".
see also: -(id)initWithConcurrencyType:(NSManagedObjectContextConcurrencyType)ct
Core Data Release Notes for OS X v10.7 and iOS 5.0
Core Data Best Practices at WWDC/2012 (requires dev account)
#CouchDeveloper, thanks for putting me on the right track!!
The final solution, wasn't to create a subclass of the NSURLConnection, but just using the completion block in combination with a simple NSURLConnection. check the following code:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length] > 0 && error == nil){
NSDictionary *dictrc = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSLog(#"de data van de rc is: %#",dictrc);
rc.id = [dictrc objectForKey:#"id"];
[self.managedObjectContext save:&error];
}
}];
I am getting data from server in applicationDidBecomeActive method.When net connection is too slow app keep crashing.I do not know how to handle this problem.any help will be appreciated.thanks in advance.
NSString *post =[[NSString alloc] initWithFormat:#"=%##=%#",myString,acMobileno];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http:///?data=%#&no=%#",myString,acMobileno]];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:postData];
NSError *error1 = [[NSError alloc] init];
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error1];
NSString *string;
if ([response statusCode] >=200 && [response statusCode] <300)
{
string = [[NSString alloc] initWithData:urlData encoding:NSMacOSRomanStringEncoding];
}
It's probably crashing because the connection has started downloading, but it hasn't finished therefore allowing the complier to pass your if statement, which would inevitably give a nil urlData parameter.
To fix this, you should be checking to see if there is an error, and then the response headers for the download. Also, I recommend running this operation on a background thread so that it doesn't block the user experience - at the moment, the app will have a delayed launch depending on the size of your file, and the user's download speed.
NSError *error1 = nil;
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error1];
NSString *string = nil;
if (error != nil && ([response statusCode] >=200 && [response statusCode] <300)){
string = [[NSString alloc] initWithData:urlData encoding:NSMacOSRomanStringEncoding];
}
else {
NSLog(#"received error: %#", error.localizedDescription);
}
For a background thread, run the above code in a dispatch_async statement, or use -sendAsynchronousRequest: instead of -sendSynchronousRequest.
Alternatively, as #Viral said, it is possible that the request is taking too long, and the app is hanging as a result of the synchronous request not finishing before the UI should have been loaded.
Most probably, it's due to synchronous call in Application's delegate method. It is taking too much time to load the UI (As internet connection is slow and you are calling the web service on main thread); and therefore OS thinks your App has hanged due to unresponsive UI and crash the App itself.
Just for debugging purpose, try the same code in your FirstViewController's viewDidAppear method. It should work fine there. And if it is so, you need to change your call to somewhere else (also, preferably in some background thread, OR Async).
EDIT: Though, If it works elsewhere, you need to change the call as Async OR on background thread for smoother UX.
I have a piece of code in the .m file as below:
- (IBAction)btnLogin:(UIButton *)sender
{
NSString *strURL = [NSString stringWithFormat:#"http://www.myworkingdomain.com/fn_checkLogin2.php?name=%#&pass=%#", self.email.text, self.password.text];
// to execute php code
NSData *dataURL = [NSData dataWithContentsOfURL:[NSURL URLWithString:strURL]];
// to receive the returend value
NSString *strResult = [[[NSString alloc] initWithData:dataURL encoding:NSUTF8StringEncoding]autorelease];
if ([strResult isEqualToString:#"0"]) {
UIStoryboard *loginFailStoryBoard = [UIStoryboard storyboardWithName:#"loginFailStoryboard" bundle:nil];
UIViewController *initialFailLogin = [loginFailStoryBoard instantiateInitialViewController];
initialFailLogin.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:initialFailLogin animated:YES];
//NSLog(#"%#", strResult);
} else {
UIStoryboard *memberMenuBoard = [UIStoryboard storyboardWithName:#"memberMenuStoryboard" bundle:nil];
UIViewController *initialMemberMenu = [memberMenuBoard instantiateInitialViewController];
initialMemberMenu.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:initialMemberMenu animated:YES];
//NSLog(#"%#", strResult);
}
NSLog(#"%#", strResult);
}
#end
I am trying to determine if a person entered the right email and password and then to move on to the next view. I have the 'strResult' in my log and it shows that the php script it working correctly. It is now seems that the if..else statement that is not working due to the part [strResult isEqualToString:#"0"]
Can someone please advice where should I change to correct this so that when a member logins with the right password, he can go to the member view?
Thanks
#dan
The main thread is where all your UI elements are displayed. If you launch a connection on the main thread, your UI will become unresponsive until the connection is resolved (Unable to press buttons, unable to scroll etc...) In order to avoid that you should use another thread. There are other ways of doing it but the following code should do the trick without blocking your UI.
NSString *loginString =[NSString stringWithFormat:#"fn_checkLogin2.php?name=%#&pass=%#", self.email.text, self.password.text];
NSURL *url = [NSURL URLWithString:#"http://www.myworkingdomain.com/"];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url ];
[urlRequest setTimeoutInterval:30.0f];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:[loginString 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 = %#",html);
}
}
];
So if data is returned you can do stuff, if connection fails you can do other stuff without blocking your UI.