I'm very new to Objective C so please bear with me. I have been searching through a lot threads but still couldn't find suitable answer for my case yet I believe this question has been asked over and over. I found a lot of tutorials how to use AppDelegate for sharing string but I can't figure out how to use it to share NSMUtableDictionary.
I want to share one NSMutableDictionary between two classes where I add data to the NSMutableDictionary in one class and read it it another. I'm using AppDelegate class to store the data.
AppDelegate.h
#import <UIKit/UIKit.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (nonatomic,retain) NSMutableDictionary *myArray;
#end
AppDelegate.m
#import "TestResults.h"
#synthesize myArray;
TestResults.h
#interface TestResults : UIViewController {
singletonObj * sobj;
}
TestResults.m
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSMutableDictionary *myArrayFromAppDelegate = appDelegate.myArray;
[myArrayFromAppDelegate setValue:#"aaa" forKey:#"bbb"];
NSLog(#"%#", myArrayFromAppDelegate);
}
When i do NSLog it return an empty array. Where did I go wrong?
I think you forget to alloc and init in AppDelegate.
In
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
myArray = [[NSMutableDictionary alloc]init];
.
.
[self.window makeKeyAndVisible];
return YES;
}
And you can give your Dictionary name : myDict it is benificial for you.
And by calling this myArray from other ViewController gives you empty NSMutableDictionary.
Oh lordy. This is indeed a VERY common beginner question.
The real answer to the question "How do I share objects in the app delegate" is "dont". It clutters up your app delegate, and makes it do work it was not intended to do. It's like storing your food from your house in your car. It works, but it weighs down the car so it doesn't work as well at it's primary job.
You should design your apps with as little global state as possible.
If you do need to share global state data, don't put it int the app delegate. Instead, create a data container singleton and use that.
You should be able to do a search on [ios] singleton and find lots of examples of creating singletons. A data container singleton is just a singleton that has properties that are used to hold and share data.
Second point:
You have an NSMutableDictionary called myArray. That is a recipe for confusion. Don't use the name of another type (the wrong type) in naming your dictionary. DO NOT DO THS! EVER! If it's not an array, don't call it an array.
Third point:
As others have pointed out, you never alloc/init your dictionary. The alloc/init should take place in the object that owns the dictionary (in your case, AppDelegate, but move it to your data container singleton.) You can ether create it in the owning class's init method, or write a custom getter that "lazy loads" the dictionary:
- (NSDictionary *)myDict;
{
if (!_myDict)
{
myDict = [NSMutableDictionary new];
}
return myDict;
}
Here you may alloc this dict.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.myArray = [NSMutableDictionary dictionary];
.....
}
Otherwise you can do in TestResults.m
NSMutableDictionary *myArrayFromAppDelegate = appDelegate.myArray;
if (!myArrayFromAppDelegate)
appDelegate.myArray = [NSMutableDictionary dictionary];
You can also do this work.
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.myDictionary=[[NSMutableDictionary alloc]init];////This could be another way
NSMutableDictionary *myDictionaryFromAppDelegate = appDelegate.myDictionary;///Don't name an dictionary by array!!!!!!!
[myDictionaryFromAppDelegate setValue:#"aaa" forKey:#"bbb"];
NSLog(#"%#", myDictionaryFromAppDelegate);
}
Thanks
Related
I have viewController with a global property 'notesArray", and I want to get it from AppDelegate through singleton.
Here is my appViewController.h
#import <UIKit/UIKit.h>
#interface AppViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
{
NSMutableArray *notesArray;
}
#property (nonatomic, strong) NSMutableArray *notesArray;
+ (AppViewController*)sharedManager;
#end
Here is my AppViewController.m
#implementation AppViewController
#synthesize notesArray;
+ (AppViewController*)sharedManager {
static AppViewController *sharedManager;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[self alloc] init];
});
return sharedManager;
}
...
#end
My app works fine, but I need to save it's state in NSUserDefaults, so in AppDelegate/applicationDidEnterBackground: I make
- (void)applicationDidEnterBackground:(UIApplication *)application {
[[NSUserDefaults standardUserDefaults] setObject:[AppViewController sharedManager].notesArray forKey:#"savedNotes"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
But the problem is that [AppViewController sharedManager].notesArray = nil.
I tried to set breakpoint to applicationDidEnterBackground: and all data from [AppViewController sharedManager] = nil.
Few observations:
notesArray is not a global property. It is a property on instances of AppViewController. Now in #2 we see that you intend it to be a singleton, but since ObjC doesn't really have a good way to prevent someone else from creating a second object of this type, are you sure you're not creating a second copy e.g. by creating an object of that type in a XIB? This is probably not your problem (see #3), but I just wanted to cover all the bases.
Your sharedManager method implies that you're seeing AppViewController as a singleton. That's an unusual thing to do with a view controller. Usually you would have several view controllers, and the singleton would be part of the model they share. Even better, you'd just give each view controller a pointer to the model object that has this array, and if you ever need a second list, everything will just work. Also, why is it called "manager"? It's a view controller.
Have you stepped through your code in the debugger to see what is going on? What value does notesArray have while you're trying to use it in applicationDidEnterBackground:? I do not see anywhere in your code that you initialize notesArray. So you just have an empty pointer to an object (i.e. notesArray is likely nil). You probably meant to add an init method to your singleton's class that creates an NSMutableArray instance and remembers its address in notesArray?
I'm having issues with dictionary, i want a dictionary create only once to save data to plist file. So how to create singleton dictionary ? Thank in advance
You can create one instance of NSDictionary in AppDelegate, make this dictionary as a public property of class.
#property (nonatomic, strong) NSMutableDictionary *myDict;
Initialize this in - (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions method.
Now access it through out the app -
((AppDelegate *) [UIApplication sharedApplication].delegate).myDict
Only one instance will be created for your use.
As you responded in comments, you need a global variable. Bellow is the receipt.
Here is what you can do:
Add variable extern definition at the top of your AppDelegate.h file:
extern NSMutableDictionary *MyGlobalDictionary;
Add variable definition at the top of your AppDelegate.m file:
NSMutableDictionary *MyGlobalDictionary;
Now you need to initialize the dictionary, can be done in initialize method of you AppDelegate. In AppDelegate.m add this somewhere in #implemetaion of AppDelegate:
+ (void)initialize {
MyGlobalDictionary = [[NSMutableDictionary alloc] init];
}
How to use:
Whenever you need it, just #import your AppDelegate.h file. Bellow is an example how to use:
NSLog(#"%#", [MyGlobalDictionary objectForKey:#"someKey"]);
Important:
While creating singleton is extremely easy it is considered to be a bad practice. Think about passing your dictionary to objects that can be interested in it. Or may be define some global class singleton if it is unavoidable.
So I've declared this in my appDelegate.h
#property(nonatomic,strong) NSMutableArray *featured;
I've synthesized it like so in my appDelegate.m
#synthesize featured;
When I log that out in the appDelegate with the object stored in there, I get the value it's supposed to have
In a viewController.h file I have declared this
#property(nonatomic,strong) NSMutableArray *featured;
In the viewController.m file I've synthesized it like this
#synthesize featured;
I then print out this line and get a null value
NSLog(#"HERE %#", featured);
That same line prints out the correct value in my appDelegate.m file. I'm completely lost. I've set it up in the way I've done it for a previous class exercise. Thanks in advance!
Edit:
I created the array in appDelegate.m file like so in a method I called loadFeatured
featured = [NSMutableArray array];
for (id dict in tempArray)
{
//NSLog(#"dict=%#",dict);
NSString *shopName = [dict objectForKey:#"shopName"];
NSString *drinkName = [dict objectForKey:#"drinkName"];
NSNumber *likes = [dict objectForKey:#"likes"];
NSNumber *dislikes = [dict objectForKey:#"dislikes"];
NSString *review = [dict objectForKey:#"review"];
Featured *feat = [[Featured alloc] initWithName:shopName drinkName:drinkName likes:likes dislikes:dislikes review:review];
NSLog(#"feat=%#\n\n",feat);
[featured addObject:feat];
}
NSLog(#"there is %d featured",[featured count]);
NSLog(#"HERE %#", featured);
Here is the way, how to access the data stored in the app delegate from your viewcontroller.
You need not synthesize the object in the viewcontroller. Just import your appdelegate file and copy the following code wherever necessary.
NSMutableArray * nArray =[ (AppDelegate*) [[UIApplication sharedApplication] delegate] featured];
The above code gives you the required array from the app delegate.Now you can make use of the nArray object to display the details in the console.
NSLog(#"%#",nArray.description);
It's hard to say how to do this without knowing the structure of your app. You could pass a pointer to the array to your view controller, if you have access to that view controller from the app delegate. The other way is to get a reference to the app delegate in your view controller, and then access its array. That can be done like this:
AppDelegate *appDel = [UIApplication shared application].delegate;
NSArray *myControllerArray = appDel.featured;
You'll need to import your app delegate into your controller's .m file to use this approach.
Since you already declared a property in appDelegate.h you can access it in the other viewController like this:
#import "appDelegate.h"
and you can access the value it by using something like this:
((AppDelegate *)[[UIApplication sharedApplication]delegate]).featured
If you need to access an NSArray or any other object in any class, via AppDelegate, just create a property to access your ViewController, like so, in your AppDelegate class:
#import "ViewController.h"
#property (nonatomic, strong) AppDelegate *appDelegate;
#property (nonatomic, strong) ViewController *viewController;
In your ViewController class:
#import "AppDelegate.h"
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
ViewController *viewControllerREFERENCE = [appDelegate viewController];
Then you'll have access to any value on your ViewController, via AppDelegate.
I hope that helps you.
I am trying to figure out how to access and display info from an object from a NSMutable array that is created in my AppDelegate.
From my AppDelegate.h
#property (readonly, retain) NSMutableArray *myRaces;
#end
AppDelegate * appDelegate;
From my AppDelegate.m
extern AppDelegate * appDelegate;
#synthesize myRaces;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
appDelegate = self;
TheRace * orc;
orc = [[TheRace alloc] init];
orc.raceTitle = #"Orc"; [orc modifyStatsFromRace:(NSString*)orc.raceTitle];
NSLog(#" test %d ", orc.massMod);
orc.raceData = #"Orcs are big burly stupid beasts";
myRaces = [[NSMutableArray alloc] init];
[myRaces addObject:orc];
}
I want to call the value of orc.massMod from another class, and cannot figure out how. I tried.
appDelegate.raceSelected = #"Orc";
NSLog(#"Orc");
appDelegate.theMass = appDelegate.myRaces.orc.massMod;
However, "appDelegate.theMass = appDelegate.myRaces.orc.massMod;" failed, the error says
... Property "orc" not found on object type 'NSMutableArray *'
How do I call that info? I want to display the value of massMod, which works in the NSLog from within appDelegate. "appDelegate.theMass" is what holds value for display in my UILabel.
Since you are storing orc in an array, you cannot access it by the name of the variable. You should be able to retrieve it by calling objectAtIndex, e.g.
appDelegate.theMass = ((TheRace*)[appDelegate.myRaces objectAtIndex:0]).massMod;
When you're in another class from Appdelegate and call "appDelegate.myRaces", you got a NSMutableArray. Thus you cannot access the orc object by appending ".orc", because orc is not an attribute of NSMutableArray.
You can write instead
// The cast is not necessary, but is useful for readability
TheRace * myOrc = (TheRace*)[appDelegate.myRaces objectAtIndex:0];
appDelegate.theMass = myOrc.massMod;
but you can easily lost track of which item goes with which index. You can use too an NSMutableDictionary :
-in your AppDelegate.h
#property (readonly, retain) NSMutableDictionary *myRaces;
-in your AppDelegate.m :
myRaces = [[NSMutableDictionary alloc] init];
[myRaces setObject:orc forKey:#"Orc"];
-in your other classes :
TheRace * myOrc = (TheRace*)[appDelegate.myRaces objectForKey#"Orc"];
appDelegate.theMass = myOrc.massMod;
Best way to do define a macro like this:
#define MyAppDelegateObject ((AppDelegate *)[[UIApplication sharedApplication] delegate])
and refer your properties in appdelegate like this:
NSLog(#"Races: %#",MyAppDelegateObject.myRaces);
I think that I am a bit confused about iOS #property getter and setters. I am trying to set an NSString iVar in my AppDelegate.h file from another class so that it can be used by all of the classes in the project?
For example, I am working on an iPhone project that stores an iVar NSString *currentUser in AppDelegate.h. I need to be able to set this through one method in a ViewController.m and then get it through another method in a second ViewController?
Maybe Getter and Setter is the wrong direction of attack all together? I understand that i don't want to alloc init the AppDelegate as the iVar will only exist in that object and I want it accessible to ALL objects in ALL classes?
Please someone set me straight.
All the best,
Darren
Here's the setup for the app delegate.
#interface AppDelegate
{
NSString *__currentUser;
}
#property (monatomic, copy) NSString* currentUser;
#end
#implementation AppDelegate
#synthesize currentUser = __currentUser;
- (void) dealloc
{
[__currentUser release];
[super dealloc];
}
#end
From one view controller, you could set a value for the current user, and from a subsequent view controller, get that value for some nefarious purpose.
#implementation LoginController
- (void) viewDidLoad
{
...
AppDelegate *bob = [[UIApplication sharedApplication] delegate];
[bob setCurrentUser: #"Jim Kirk"];
...
}
#end
In some other view controller that appears later, the value of the current user can be accessed.
#implementation ProfileViewController
- (void) viewDidLoad
{
...
AppDelegate *bob = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSString * user = [bob currentUser];
// insert nefarious purpose for current user value here
...
}
#end