Cocoa – AppDelegate Being accessed from different classes, from RootController.m

cocoadidselectrowatindexpathnsdictionaryretained-in-memoryvariables

My problem is in getting the information from a controller(which happens to be my rootViewController) to another view. In trying to get access to it through the app delegate, I was not able to get it working. I have found how to do this, but this inturn created another problem of getting the view inside a modal view controller to actually display the data. Below I have posted both the appDelegate information, and an NSMutable Dictionary solution code for those that may need help too.

I had tried for more than a week to solve ths problem on my own. My issue ended up being how to access the appDelegate, which was why I was having a problem with the NSDictionary. So in the end the issue wasn't the NSDictionary, although had I gone further it would have been a problem I am sure.

Firstly I would like to thank TechZen for helping me to see I was over programming, and point me in the right direction.

Here is what I learned.

Assign your variable in the appDelegate.

AppDelegate.h

 @interface AppDelegate : NSObject  < UIApplicationDelegate, UINavigationControllerDelegate >
{   
    UIWindow *window;
    UINavigationController *navController;

    // Array to store the Makers Objects
    NSMutableArray *makers;

}

@property (nonatomic, retain) IBOutlet UIWindow *window; 
@property (nonatomic, retain) IBOutlet UINavigationController *navController;

@property (retain, nonatomic) NSMutableArray *makers;
@end

AppDelegate.m

    - (void)applicationDidFinishLaunching:(UIApplication *)application 
{

    makers = [[NSMutableArray alloc] init] ;
}

In the ViewController.m assign the variable to the appDelegate. I did this inside a tableView function didSelectRowAtIndexPath.

        AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

// The line below loads it into the variable, the one above takes it and makes it available for other view Controllers.

       Maker *maker = (Maker *)[appDelegate.makers objectAtIndex:indexPath.row];

// the combination of the above and below also loads the information into another array in this view controller, defined in an NSObject Controller called Maker (.h and .m files)

      maker = [self.Makers objectAtIndex:indexPath.row];

Now in your view Controller you want to load the variable from the appDelegate, set it up like this.

#import <UIKit/UIKit.h>

#import "AppDelegate.h"
#import "Maker.h"

@class AppDelegate;

@interface DetailsViewController : UIViewController
{

        AppDelegate *dappDelegate;
    DetailsViewController *detailsView;

    IBOutlet UITextView *makerDescription;

}

@property (retain, nonatomic) AppDelegate *dappDelegate;

@property (nonatomic, retain) DetailsViewController *detailsView; 

@property (nonatomic, retain) IBOutlet UITextView *makerDescription;

@end

and in the viewController.m file;

#import "DetailsViewController.h"
#import "AppDelegate.h"

@synthesize dappDelegate;

- (void)viewWillAppear:(BOOL)animated // or ViewDidLoad not sure which is better.

    dappDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
    NSString *newLocalVariable = [dappDelegate.makers description];

NSLog(@"newLocalVariable: %@", [dappDelegate.makers description]);
// This is for verifying you have it loaded. 'description' is defined in the Maker NSObject, see below for those files, and above for where it was assigned originally

….. and assign it to what ever you want now!

I hope this helps everyone. You could at this point drop the NSArray from there into a NSDictionary, but the access is now with keys and values, so a bit more complicated at this point to access, but of course advantages. I just cannot get that totally down yet, and have backed away from that method to just use a NSArray for now.

Below is a sample of a Makers h and m file for you to see as well.

Maker.h

@interface Maker : NSObject 
{
    NSString *name;
    NSString *address;
    NSString *city;
    NSString *postalcode;
    NSString *country;
    NSString *phonenumber;
    NSString *email;
    NSString *description;
    NSString *services;
    NSString *website;
}

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *address;
@property (nonatomic, copy) NSString *city;
@property (nonatomic, copy) NSString *postalcode;
@property (nonatomic, copy) NSString *country;
@property (nonatomic, copy) NSString *phonenumber;
@property (nonatomic, copy) NSString *email;
@property (nonatomic, copy) NSString *description;
@property (nonatomic, copy) NSString *services;
@property (nonatomic, copy) NSString *website;

- (id)initWithName:(NSString *)n address:(NSString *)a city:(NSString *)c postalcode:(NSString *)z country:(NSString *)o phonenumber:(NSString *)p email:(NSString *)e description:(NSString *)d services:(NSString *)s website:(NSString *)w;

@end

and its Maker.m file;

#import "ViolinMaker.h"

@implementation Maker
@synthesize name, address, city, postalcode, country, phonenumber, email, description, services, website;

- (id)initWithName:(NSString *)n address:(NSString *)a city:(NSString *)c postalcode:(NSString *)z country:(NSString *)o phonenumber:(NSString *)p email:(NSString *)e description:(NSString *)d services:(NSString *)s website:(NSString *)w; 
{
    self.name = n;
    self.address = a;
    self.city = c;
    self.postalcode = z;
    self.country = o;
    self.phonenumber = p;
    self.email = e;
    self.description = d;
    self.services = s;
    self.website = w;
    return self;
}
@end

I hope this helps others get this straight, as it really cost me a lot of time and I hope you can get a bit from what I learned.

Sincerely,
Kirk

Best Answer

I don't see any point where you populate the selectedMaker ivar in the DetailsViewController with data from the app delegate's `selectedMaker'. Just because they have the same name doesn't mean they share the same data.

You need to assign or copy the values from the app delegate to the view controller. The quick and dirty way is to do something like:

@implementation DetailsViewController

...

-(void) viewDidLoad{
//selectedMaker=[[UIApplication sharedApplication] selectedMaker]; <-- this is wrong
//Edit, this is the correct call to app delegate
selectedMaker=[[[UIApplication sharedApplication] delegate] selectedMaker]; 
}

Edit01:

So... at the suggestion of TechZen i have tried to move my NSDictionary out of my RootViewController.

(1) I'm not sure why you have the SelectedMaker class of what it is supposed to accomplish. You seem to have confused the class with a NSMutableDictionary. The class doesn't have any apparent reason to return a dictionary containing the values of it's own iVars. This is completely redundant. You already appear to have a class called ViolinMaker that encapsulates all the data for each violin maker record.

(2) The initializations methods for SelectedMaker are not implemented correctly. It looks like you have to call -[SelectedMaker initWithsName:..] before you call -[SelectedMaker init] otherwise init has no clue what the keys are.

(3) In any case, in the didSelectRow method, you don't actually initialize an instance of SelectedMaker. This line:

SelectedMaker *selectedMaker = [[[NSMutableDictionary alloc]retain] initWithObjects: objects forKeys: keys];

Does not create an instance of SelectedMaker but rather a NSMutableDictionary and more or less cast it to the SelectedMaker class. This is why you get the compiler warning from the for-loop.

(4) I don't see any need for the SelectedMaker class at all. These lines:

ViolinMakerAppDelegate *appDelegate = (ViolinMakerAppDelegate *)[[UIApplication sharedApplication] delegate];
ViolinMaker *violinMaker = (ViolinMaker *)[appDelegate.violinMakers objectAtIndex:indexPath.row];
violinMaker = [self.filteredViolinMakers objectAtIndex:indexPath.row];

Appear to provide you all the information you need to populate any particular row in your table or your detail view. You can use these three line in any view in which you need to access the data in appDelegate.violinMakers'. TheviolinMaker`object contains all the data you need.

I think you are making this more complicated than it has to be. All you need is (A) a class that encapsulates the data for each record fetched from your SQL. In this case it looks like ViolinMaker does this. (B) You need an array (or other collection) in the app delegate to store the multiple ViolinMaker instances. (C) You need code in each viewcontroller that will access the array/collection in the app delegate so that the viewcontroller can select the ViolinMaker instances it needs.

Edit02:

no I cannot use those 3 lines, as the 'objectAtIndex:indexPath.row' is only available inside the didSelectRow function. Which means I would have to reconstruct the table and all of its data.

You just define the dictionary once as an instance variable in your app delegate. So, you would have a mutable dictionary where each value was ViolinMaker and each key was some attribute of a violin maker such as the name. Let's call that violinMakersDict.Then, anywhere in your app, you would access the dictionary by first calling the app delegate and the accessing the violinMakersDict.

To populate a table, you would need to extract the some values as an array. Most likely, you would takes the keys which are names of violin makers. Then you would sort the array alphabetically, then you would populate each row with the value in the array at the index.row value.

Likewise, if you need to add data in one view, you can write to the violinMakersDict and then access that data from another view by again calling the app delegate.