Problems calling performSegueWithIdentifier from connectionDidFinishLoading - ios

I have a login screen and when a button is pressed this code executes:
- (IBAction)btnLogin:(id)sender {
[username resignFirstResponder];
[password resignFirstResponder];
Auth *authRequest = [[Auth alloc] init];
NSURL *url = [[NSURL alloc] initWithScheme:#"http" host:APIHOST path:#"/authenticate"];
[authRequest setUrl:url];
[authRequest setUsername:[username text]];
[authRequest setPassword:[password text]];
[authRequest authenticate];
[self performSegueWithIdentifier:#"Login2Tab" sender:self];
}
This works well and the segue executes as expected (So I know the segue exists and its identified correctly in the storyboard). I however don't want to perform the segue until the connection has finished loading so this implementation file is my delegate for NSURLConnection and at the bottom I have (and the if/else on responseCode does work)..:
// Close the connection
- (void)connectionDidFinishLoading:(NSURLConnection*)connection {
if(responseCode == 200) {
[self performSegueWithIdentifier:#"Login2Tab" sender:self];
} else {
NSLog(#"Bad.. bad.. bad...");
}
NSLog(#"Connection Closed.");
}
(When I put the performSegueWithIdentifer in the connectionDidFinishLoading I comment out the performSegueWithIdentier from above...).
But now the app always crashes stating:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver (LoginViewController: 0x7163640) has no segue with identifier 'Login2Tab''
Which is where I am stuck because it works when its not called from within the connectionDidFinishLoading...

Just wanted to say I have this resolved altho I am not entirely sure how.
Basically this line in my ViewController
[authRequest authenticate];
Was loading a method in an external class file:
- (void)authenticate {
NSURL *url = [[NSURL alloc] initWithScheme:#"http" host:APIHOST path:#"/authenticate"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:3];
NSString *postString = [[NSString alloc] initWithFormat:#"email=%#&password=%#", [username text], [password text]];
[request setValue:APIKEY forHTTPHeaderField:#"apikey"];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
[request setURL:url];
if ([NSURLConnection canHandleRequest:request]) {
NSLog(#"Starting network connection");
NSURLConnection *myConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[myConnection start];
} else {
//Error cannot activate network request from device
}
}
The delegates for the NSURLConnection were in the viewcontroller... and I could see them being accessed when the method was complete however I couldn't perform the segue from them. By moving this function from the class file to my viewcontroller I was able to call my segue's from the delegate method of connectionDidFinishLoading....
Strange but worked.

Related

uiwebview not working with HTTP or HTTPS urls

i am trying to load a url in uiwebview in xcode and it is loading just fine but problem is that there is ZERO user interaction with it. i can not touch any button on it or can not even scroll it. i have already tried Allow Arbitrary Loads = YES in info.plist but nothing happened here is my code.
[webPage setDelegate:self];
[webPage loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://url.com"]]];
[webPage addSubview:activityIndicatorView];
here is more code from .h:
#interface ViewController : UIViewController<UIWebViewDelegate>{
IBOutlet UIWebView *webPage;
}
#property (retain, nonatomic) IBOutlet UIWebView *webPage;
its s simple uiwbview from interface builder in a simple uiviewcontroller.
and here is my info.plist
I figure out that problem might be here in this part of the code. there is a menu in my uinavigationbar as well which is loading xml menu. wait i will post my code.
- (void) makeMenu{
#try {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0),^{
NSURL *url=[NSURL URLWithString:#"http://url/xml-menu.php"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
NSError *error = nil;
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if ([response statusCode] >=200 && [response statusCode] <300)
{
rssOutputData = [[NSMutableArray alloc]init];
xmlParserObject =[[NSXMLParser alloc]initWithData:urlData];
[xmlParserObject setDelegate:self];
[xmlParserObject parse];
}
dispatch_async(dispatch_get_main_queue(), ^{
sideMenu.delegate = self;
NSInteger count;
NSMutableArray *itemsArry = [[NSMutableArray alloc] init];
count = [rssOutputData count];
for (int i = 0; i < count; i++){
BTSimpleMenuItem *item = [[BTSimpleMenuItem alloc]initWithTitle:[[rssOutputData objectAtIndex:i]xmltitle] image:[UIImage imageNamed:#"arrow.png"]
onCompletion:^(BOOL success, BTSimpleMenuItem *item) {
[webPage loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[[rssOutputData objectAtIndex:i]xmllink]]]];
}];
[itemsArry addObject:item];
}
NSArray *itemSarry=[[NSArray alloc] initWithArray:itemsArry];
sideMenu = [[BTSimpleSideMenu alloc]initWithItem:itemSarry addToViewController:self];
});
});
}
#catch (NSException * e) {
NSLog(#"Exception: %#", e);
}
}
when i call this method in viewdidload uiwebview stop responding. And if i do not call this part uiwebview works just fine. please help me i need this menu as well.
Try this:
webPage.userInteractionEnabled = YES;

non-main thread does not wait for delegate methods to finish

I am trying to use threads in my app to call web service and get data from
connectionDidFinishLoading
delegate method, but non-main thread does not wait for delegate methods to finish.
here is code for thread :
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Here your non-main thread.
[self Login_WS];
//sleep(10);
dispatch_async(dispatch_get_main_queue(), ^{
//Here you returns to main thread.
//[MMProgressHUD updateProgress:1.f];
[MMProgressHUD dismissWithSuccess:#"done"];
DetailOfMenu *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"detail"];
//[colorVC setColor:color];
UINavigationController *colorNavi1 = [[UINavigationController alloc] initWithRootViewController:detail];
[self.qp_splitViewController setRightController:colorNavi1];
MenuTableView *colorVC = [self.storyboard instantiateViewControllerWithIdentifier:#"menu"];
//[colorVC setColor:color];
UINavigationController *colorNavi = [[UINavigationController alloc] initWithRootViewController:colorVC];
[self.qp_splitViewController setLeftController:colorNavi];
});
});
and here is code for web service :
-(void)Login_WS{
flag =1;
NSString* ENC_UserName = [self ENC:Username.text];
NSString* ENC_Password = [self ENC:Password.text];
NSString *envelopeText = [NSString stringWithFormat: #"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\">\n"
"<soap12:Body>\n"
"<GetReportsNames xmlns=\"http://tempuri.org/\">\n"
"</GetReportsNames>\n"
"</soap12:Body>\n"
"</soap12:Envelope>\n",];
//envelopeText = [NSString stringWithFormat:envelopeText, txt1.text];
NSData *envelope = [envelopeText dataUsingEncoding:NSUTF8StringEncoding];
//NSLog(#"URL in Call_WS in SplashScreen %#",Var.url);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
[request addValue:#"http://tempuri.org/GetReportsNames" forHTTPHeaderField:#"SOAPAction"];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:envelope];
[request setValue:#"application/soap+xml; charset=utf-8"
forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%d", [envelope length]]forHTTPHeaderField:#"Content-Length"];
// fire away
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (connection)
responseData = [NSMutableData data];
else
NSLog(#"NSURLConnection initWithRequest: Failed to return a connection.");
}
note: it is working fine in non-threading mood.
You should do the UI part that is currently after you call [self Login_WS]; when the web service call returns a response or a failure.
As the webservice call is also async the Login_WS method returns immediately after you create it, so your UI code will get called there. Instead implement the NSURLConnectionDelegate protocol and update your UI in:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
or:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
Not sure exactly at what point in code you want to wait and what part you want to prevent for getting executed. You issue is of synchronization category where one thread has to wait till other part finishes work. For this you can use NSCondition. You will find good examples on google for this :-)

How to use a single connection to the network in several classes

Guys help deal with a problem I'm new to programming, and accordingly I have a problem in general, I do not know how to work with the network! I wrote a class in which I log into the site after login I can not use the connection to send other requests in other views!!!
here's what I wrote:
- (IBAction)loginClicked:(id)sender {
#try {
if([[txtUserName text] isEqualToString:#""] || [[txtPassword text] isEqualToString:#""] ) {
[self alertStatus:#"Пожалуйста заполните все поля!!!" :#"Авторизация не удолась!"];
} else {
NSString *post =[[NSString alloc] initWithFormat:#"login=%#&pass=%#",[txtUserName text],[txtPassword text]];
NSURL *url=[NSURL URLWithString:#"http://chgu.org/?mobile=1"];
NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding 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];
NSError *error = [[NSError alloc] init];
NSHTTPURLResponse *response = nil;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if ([response statusCode] >=200 && [response statusCode] <300)
{
NSData *responseData = [[NSData alloc]initWithData:urlData];
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:nil];
if([jsonObject objectForKey:#"error"])
{
[self alertStatus:#"Ошибка ввода данных" :#"Ю хав а трабл"];
} else {
[self alertStatus:#"Авторизация прошла успешно" :#""];
}
} else {
if (error) NSLog(#"Error: %#", error);
[self alertStatus:#"Connection Failed" :#"Login Failed!"];
}
}
}
#catch (NSException * e) {
NSLog(#"Exception: %#", e);
[self alertStatus:#"Login Failed." :#"Login Failed!"];
}
[txtUserName resignFirstResponder];
[txtPassword resignFirstResponder];
}
how do I use this connection when I move to a different view!
Please tell me anything all night sitting at the computer can not figure out
You have the right idea, assuming that you want relatively tight coupling between these controllers via that delegate protocol.
Since neither controller knows about the other until that delegate property is set you need to have some object which has a reference to both of them wire up that relationship. In your case that's probably the application delegate which can create both controllers, set one as the delegate of the other, and pass both along to your tab bar controller.
What you might actually want is to have the app delegate give both controllers a reference to some shared model object. Your FirstViewController can update that model when you tap a button and your SecondViewController can observe changes to the model to update it's display (or just update its view when it appears based on the current model state). That way your controllers don't need to know anything about each other.
in secondVC, define something like the following:
#protocol secondVCDelegate
#interface secondVC : UIViewController
#property (nonatomic, assign) id<secondVCDelegate> delegate;
#end
#optional
-(void)someDelegateMethod:(secondVC*)viewController;
#end
at the time of creating the instance of secondVC you must assign the delegate property of secondVC to self! something like this:
// in firstVC
secondVC vc = [[secondVC alloc]...];
vc.delegate = self;
[navcontroller pushVC:vc];
the line vc.delegate = self; does the trick.
hope it helps...

IOS Creating Utility Class For NSURL

I have a project I am working on that has a number of different news feeds and announcement boards that displays post from various sources. Currently I have the code for the like, delete and flag buttons in methods contained in each class file for the views that display the feeds. I have been trying to craft a utility class that allows me to place the code for the three functionalities listed above in one object to be used throughout the project. I have done the exact same type of thing in C++ or Java, but am having issues reproducing it in objective-c. The like, delete and flag buttons use the NSURL libraries to interact with the web service. Bellow is an example of one of the methods I am trying to implement in the utility class, and is the code used to be implemented in the like buttons:
+ (void)btnLikeAction:(UIButton *)btnLike userIdString:(NSString *)userId contentSource:(NSString *)sourceId cellUsed:(NewsfeedCell *)cell dataForCell:(Post *)post
{
BOOL hasLiked = post.hasLiked;
UILabel *likeLabel = cell.likeCount;
NSString *pId = post.postId;
if (hasLiked)
{
[btnLike setEnabled:NO];
NSString *url = [NSString stringWithFormat: #"http://192.155.94.183/api/v1/likes/unlike/%#/%#/%#/", sourceId, pId, userId];
NSURL *URL = [NSURL URLWithString:url];
NSURLRequest *request = [NSURLRequest requestWithURL:URL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
int localCount = [likeLabel.text intValue];
localCount--;
likeLabel.text = [NSString stringWithFormat:#"%i", localCount];
post.likeCount = [NSString stringWithFormat:#"%i", localCount];
post.hasLiked = NO;
[btnLike setEnabled:YES];
}
else
{
[btnLike setEnabled:NO];
NSError *err = nil;
NSDictionary *likeData = [NSDictionary dictionaryWithObjectsAndKeys:
userId, #"user_id",
pId, #"source_id",
sourceId, #"source",
nil];
NSData *JSONLike = [NSJSONSerialization dataWithJSONObject:likeData options:NSJSONWritingPrettyPrinted error:&err];
NSString *url = #"http://192.155.94.183/api/v1/likes.json";
NSURL *URL = [NSURL URLWithString:url];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30.0];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%d", [JSONLike length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:JSONLike];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
int localCount = [likeLabel.text intValue];
localCount++;
likeLabel.text = [NSString stringWithFormat:#"%i", localCount];
post.likeCount = [NSString stringWithFormat:#"%i", localCount];
post.hasLiked = YES;
[btnLike setEnabled:YES];
}
}
This code uses a web service to update the number of likes for a specific piece of content. It works when the method is placed into the individual ViewController class files, but when I try to make a utility class with the individual methods I run into issues with the didReceiveAuthenticationChallenge, didReceiveResponse, didReceiveData and connectionDidFinishLoading methods not being called. Originally, I assumed that the delegate methods would be called in the file that that the utility methods were called in. But that was not the case. When I implemented the method definitions in the actual utility class, the methods still weren't called. I did some research on the topic and looked into this article but found I was unable to find substantial resources that helped my specific situation. How do I set up my utility class? I can post the full code of the utility if needed.
As #serrrgi already said, the problem is that btnLikeAction:... is a class method, so that self is the class itself. You have the following options:
Make all delegate methods class methods, e.g.
+ (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"didReceiveResponse");
}
Create an instance of your Utility class and use that as delegate:
YourClass *u = [[self alloc] init];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:u];
Use sendAsynchronousRequest:..., which does not need a delegate:
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (data != nil) {
NSLog(#"success");
} else {
NSLog(#"error: %#", error);
}
}];

XCode: Stop Animating Activity Indicator after Executing a Method From Another File

Ok, this seems like it should be very simple - All I want to do is call my ServerConnect.m (NSObject), NSURL Connection Request Method, from my SignIn.m (ViewController) and stop the UIActivityIndicatorView after the NSURL Request has completed. Of course, if I do it all on the main thread:
- (IBAction)forgotPassword:(id)sender {
[activityIndicator startAnimating];
connection = [[ServerConnect alloc] init];
[connection sendUserPassword:email withSecurity:securityID];
[activityIndicator stopAnimating];
}
Then, everything will then execute concurrently, and the activity indicator will start and stop before the connection method finishes...
Thus, I attempted to place the connection request on a secondary thread:
- (IBAction)forgotPassword:(id)sender {
[NSThread detachNewThreadSelector: #selector(requestNewPassword:) toTarget:self withObject:userEmail.text];
}
- (void) requestNewPassword:(NSString *)email
{
[self->thinkingIndicator performSelectorOnMainThread:#selector(startAnimating) withObject:nil waitUntilDone:NO];
//Make NSURL Connection to server on secondary thread
NSString *securityID = [[NSString alloc] init];
securityID = #"security";
connection = [[ServerConnect alloc] init];
[connection sendUserPassword:email withSecurity:securityID];
[self->thinkingIndicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:NO];
}
But, I don't see the activity indicators here either, which may be due the NSURL Request not functioning properly on the secondary thread (i.e. for some reason, it does not gather an xml string as it does when requested on the main thread).
What is the proper way to architecture my code to make this work? I am surprised at how much work has been involved in trying to figure out how to get my activity indicator to simply stop after a method from another file has finished executing. Is there a way to run the code in series (one after another) and not concurrently? Any help would be appreciated.
Updated to Show: sendUserPassword:(NSString *)withSecurity:(NSString *)
- (void)sendUserPassword:(NSString *)emailString
withSecurity:(NSString *)passCode;
{
NSLog(#"Making request for user's password");
newUser = NO;
fbUser = NO;
forgotPassword = YES;
NSString *post = [NSString stringWithFormat: #"email=%#&s=%#", emailString, passCode];
NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding];
//Construct the web service URL
NSURL *url = [NSURL URLWithString:#"http://www.someurl.php"];
//Create a request object with that URL
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:90];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:postData];
//Clear out the existing connection if there is one
if(connectionInProgress) {
[connectionInProgress cancel];
}
//Instantiate the object to hold all incoming data
xmlData = [[NSMutableData alloc] init];
//Create and initiate the conection - non-blocking
connectionInProgress = [[NSURLConnection alloc] initWithRequest: request
delegate:self
startImmediately:YES];
}
One suggestion try like this:
- (IBAction)forgotPassword:(id)sender
{
[self->thinkingIndicator startAnimating];
[NSThread detachNewThreadSelector: #selector(requestNewPassword:) toTarget:self withObject:userEmail.text];
}
- (void) requestNewPassword:(NSString *)email
{
//Make NSURL Connection to server on secondary thread
NSString *securityID = [[NSString alloc] init];
securityID = #"security";
connection = [[ServerConnect alloc] init];
[connection sendUserPassword:email withSecurity:securityID];
[self->thinkingIndicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:NO];
}
I ended up incorporating the NSNotification system (see Multithreading for iOS) to solve my problem. Any reason why this would be frowned upon:
"One easy way to send updates from one part of your code to another is Apple’s built-in NSNotification system.
It’s quite simple. You get the NSNotificationCenter singleton (via [NSNotificationCenter defaultCenter]) and:
1.) If you have an update you want to send, you call postNotificationName. You just give it a unique string you make up (such as “com.razeware.imagegrabber.imageupdated”) and an object (such as the ImageInfo that just finished downloading its image).
2.) If you want to find out when this update happens, you call addObserver:selector:name:object. In our case the ImageListViewController will want to know when this happens so it can reload the appropriate table view cell. A good spot to put this is in viewDidLoad.
3.) Don’t forget to call removeObserver:name:object when the view gets unloaded. Otherwise, the notification system might try to call a method on an unloaded view (or worse an unallocated object), which would be a bad thing!"
You could try something this, it uses a block when it is finished. I had similar thing right here.
// Turn indicator on
// Setup the request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
[request setTimeoutInterval: 90.0];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:postData];
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue currentQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
// Its has finished but sort out the result (test for data and HTTP 200 i.e. not 404)
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (data != nil && error == nil && [httpResponse statusCode] == 200)
{
// Connection finished a gooden
// Do whatever you like with data
// Stop indicator
}
else
{
// There was an error, alert the user
// Do whatever you like with data
// Stop indicator
}
}];

Resources