IOS: global property variable not set even when value is passed to it

I have a set of code that parses an XML file to find a user ID. This identification number is sent to another view controller for a SOAP WSDL request. The problem is that the value is never set.

My two classes are LoginPageViewController and EMIMenuTableViewController.

I am trying to pass an integer value from LoginPageViewController to EMIMenuTableViewController.

Here is the header file for EMIMenuTableViewController:

#import <UIKit/UIKit.h>
@interface EMIMenuTableViewController : UITableViewController
@property (nonatomic) int userNumber;
@end

      

Here is the header file for LoginPageViewController:

#import <UIKit/UIKit.h>

@interface LoginPageViewController : UIViewController <NSXMLParserDelegate>

{
    NSXMLParser *xmlparser;

    NSString *classelement;
    NSMutableArray *titarry;
    NSMutableArray *linkarray;
    bool itemselected;
    NSMutableString *mutttitle;
    NSMutableString *mutstrlink;
    int userNumber;
    NSString *parsedString;
}

// Username and password text fields
@property (weak, nonatomic) IBOutlet UITextField *usernameTextField;
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField;

// Logging in and registering
- (IBAction)loginButtonSelected:(id)sender;
@property (weak, nonatomic) IBOutlet UIButton *registerButton;

// Unwind segue IBAction for logging out
- (IBAction)logoutAndUnwind:(UIStoryboardSegue *)segue;

@end 

      

Here the value is set in LoginPageViewController:

- (void)parserDidEndDocument:(nonnull NSXMLParser *)parser {
EMIMenuTableViewController *controller = [[EMIMenuTableViewController alloc] init];
// If the user is authenticated, allow them to proceed with the segue
if([parsedString containsString:@"User Authenticated"]) {

    >>>controller.userNumber = userNumber<<<; - set here
    NSLog(@"User:%d", userNumber);
    [self performSegueWithIdentifier:@"login_success" sender:self];
    NSLog(@"Logged In");

    // If the server says that the user isn't found, notify the user to create an account.
} else if([parsedString containsString:@"User Not Found"]) {

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"User Not Found" message:@"Please create an account!" delegate:self cancelButtonTitle:@"Close" otherButtonTitles:nil, nil];
    [alert show];

    // If the server replies that the password is incorrect, notify the user.
} else if([parsedString containsString:@"Password Incorrect"]) {

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Incorrect Username/Password" message:@"Plese try again." delegate:self cancelButtonTitle:@"close" otherButtonTitles:nil, nil];
    [alert show];

} else {

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"An error has occurred!" message:@"Please check your network connection and try again." delegate:self cancelButtonTitle:@"close" otherButtonTitles:nil, nil];
    [alert show];

}

}

      

Here is the code that shows that the value is not set (this is in the EMIMenuTableViewController):

NSLog(@"User: %d", _userNumber);

      

Also, here is the console output. You can see the value is set in LoginPageViewController where I registered it, but when registered in EMIMenuTableViewController it is 0.

2015-07-08 15:09:41.151 EMI[1971:173596] User:27808
2015-07-08 15:09:41.179 EMI[1971:173596] Logged In
2015-07-08 15:09:41.186 EMI[1971:173596] User: 0

      

I figured I was doing it right, but I can't figure out what is wrong. Am I setting a variable incorrectly?

Thank.

+3


source to share


3 answers


This line ...

EMIMenuTableViewController *controller = [[EMIMenuTableViewController alloc] init];

      

creates EMIMenuTable vc and then this line

controller.userNumber = userNumber;

      

sets a property on it. But this controller only lives for milliseconds. It is immediately released by ARC when it goes beyond.

Meanwhile, it performSegue

creates a new copy of EMIMenuTableVC, and it never gets a chance to hear about what userNumber

it finds in the parsing.

So we need to get this value from the parsing passed to the segue view controller. A simple way of doing this would be to declare userNumber as a property of LoginVC, for example

@interface LoginPageViewController : UIViewController <NSXMLParserDelegate>
// ...
// more formal than your from the EMIMenuTable vc, but equivalent...
@property (nonatomic, assign) NSInteger userNumber;

      

That being said, your parser should not create its own EMIMenuTable vc. Just set the property to yourself ...



if([parsedString containsString:@"User Authenticated"]) {
    // ...
    self.userNumber = userNumber;
    // ...
    [self performSegueWithIdentifier:@"login_success" sender:self];

      

Now, prepare for the segue and set the target vc.

EDIT - changed this with the understanding that the segue is pointing to the navigation view controller ...

// be sure to import
#import "EMIMenuTableViewController.h"
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"login_success"]) {
        UINavigationController *navVC = (UINavigationController *)segue.destinationViewController;
        EMIMenuTableViewController *vc = (EMIMenuTableViewController *)navVC.viewControllers[0];
        // finally
        vc.userNumber = self.userNumber;
    }
}

      

A slightly bolder approach is to misuse the parameter sender:

performSegue

. Then you can loop through the instance variable for LoginVC and just pass the value straight like this ...

[self performSegueWithIdentifier:@"login_success" sender:@(userNumber)];

      

This is weird because the performSegue sender is a number. But it will work since the type is generic id

. Then your preparation method looks the same except ...

    if ([segue.identifier isEqualToString:@"login_success"]) {
        UINavigationController *navVC = (UINavigationController *)segue.destinationViewController;
        EMIMenuTableViewController *vc = (EMIMenuTableViewController *)navVC.viewControllers[0];
        NSNumber *paramTrick = (NSNumber *)sender;
        vc.userNumber = [paramTrick intValue];
    }

      

But I think I do not recommend this path, especially if you are just starting out.

+2


source


EMIMenuTableViewController * controller = [[EMIMenuTableViewController alloc] init]; will create a new instance and in your code that you are not actually using, that means you are representing some other instance of EMIMenuTableViewController.

you need to get a reference to this EMIMenuTableViewController instance and then set only userNumber for that instance only

If you are using storyboard with segue to present EMIMenuTableViewController the process is as follows

1) give the segue id for the above segue (here it is "segue_to_emimenutable")

2) declare a global variable named "userNumber" and update userNumber in the parserDidEndDocument method



3) then in the segue preparation method go to the view of the EMIMenuTableViewController instance

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"segue_to_emimenutable"]) {
    EMIMenuTableViewController *destinationViewController = segue.destinationViewController;
    destinationViewController.userNumber = self.userNumber;
}

      

}

if you imagine any other method the storyboard with segue mentions this in the question so my answer can be modified to do this

+4


source


The instance controller

you created and set the value is not the one used when the storyboard is displayed.

You should do this in prepareForSegue

..
1. Store the username in a global property in the LoginViewController. 2. Inject prepareForSegue

and set the value to your MenuTableViewController

Something like below

    controller.userNumber = userNumber  //Delete this
  // Instead of the above line line,Store it in your property.. 
    self.userNumber = userNumber



- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 
{
    if ([segue.identifier isEqualToString:@"login_success"]) {
        EMIMenuTableViewController *destViewController = segue.destinationViewController;
        destViewController.userNumber = self.userNumber;
    }
}

      

+2


source







All Articles