Problems in getting an array from NSUserDefaults - objective-c

I am making a game using cocos2d-iphone and would like to make a screen with best previous scores. I have two scenes. In the MainScene I made a global variable for storing a score, that is obviously changing during the game; one mutable array and one simple array that will be a duplicate for the mutable one:
NSInteger _scoreValue;
NSMutableArray *_scoresMutable;
NSArray *_scores;
In the same class, when game ends, I add new score to the mutable array, make a static duplicate and save it in NSUserDefaults:
[_scoresMutable addObject:#(_scoreValue)];
_scores=[NSArray arrayWithArray:_scoresMutable];
[[NSUserDefaults standardUserDefaults] setObject:_scores forKey:#"gameScores"];
Then, in other class of scene with scores called bestscores(I don't know how is better, but it was easier for me to make just a new scene, because I am using SpriteBuilder) I import MainScene.h just in case and make a label.
At the moment I am trying to get all scores from NSUserDefaults, to sort it and to show second biggest value. But it always shows 0 (label is empty by default). So, how to make that's all right?
- (void)didLoadFromCCB {
NSSet *numberSet = [NSSet setWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"gameScores"]];
NSArray *sortedNumbers = [[numberSet allObjects] sortedArrayUsingDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"self" ascending:NO] ]];
NSNumber *secondHighest;
if ([sortedNumbers count] > 1){
secondHighest = sortedNumbers[1];
}
[_secondBiggestLabel setString:[NSString stringWithFormat:#"%ld",(long)secondHighest]];
}
`
EDIT : synchronize didn't help. Maybe I need to write something else to get access to NSUserDefaults from another one class?

When you set the object you have to follow it with a call to synchronize. That will actually save it.
[[NSUserDefaults standardUserDefaults] synchronize];

Are you sure you've initialized that '_scoresMutable' array?
Also, if the code from the below section is ran on a separate launch than when the first section of code is ran, it's possible you're terminating the app before the defaults can write to disk. Call 'synchronize' on the NSUserDefaults instance to fix that.

Related

Public Array? Making array accessible by multiple classes

I am trying to save lessons in a day (from text-fields) into an array so that I can save them, and later display them on a new view.
My current code:
//monLessons = Monday's lessons
NSMutableArray *monLessons;
monLessons = [NSMutableArray arrayWithObjects: self.mon1.text, self.mon2.text, self.mon3.text, self.mon4.text, self.mon5.text, self.mon6.text, nil];
Cheers!
If it is just this one NSMutableArray that you want to access from multiple classes - NSUserDefaults would be the easiest and cleanest solution.
Example
So save the NSMutableArray:
[[NSUserDefaults standardUserDefaults]setObject:YOUR ARRAY forKey:#"MondayLessons"];
Now NSUserDefaults returns a immutable copy of the a array when you ask for it - you will need to return it like this:
NSMutableArray *yourArray = [[[NSUserDefaults standardUserDefaults]objectForKey:#"MondayLessons"] mutableCopy];
This way you get an NSMutableArray back and not an NSArray
Edit 2
I forgot to add:
Before you close your application or move to a new ViewController - you need to save the data to NSUserDefaults So please call - [[NSUserDefaults standardUserDefaults]synchronize];
Anytime you want to save. If you don't call this line of code - when you ask NSUserDefaults for your Array - it will be nil. You need to do this every time you make a change to the array you're saving.

Updating values on NSArray

I'v run into such problem. I need to update the values in my NSArray. And don't know a way to do it. Here's my array
NSArray *arrayWithInfo = [[NSArray alloc] initWithObjects:AMLocalizedString(#"Status", nil),AMLocalizedString(#"Call", nil),AMLocalizedString(#"Location", nil),AMLocalizedString(#"Control", nil),AMLocalizedString(#"Sim", nil),AMLocalizedString(#"Object", nil),AMLocalizedString(#"Info", nil),nil];
self.dataArray = arrayWithInfo;
[arrayWithInfo release];
To be more specific I have tableview initialized with this array. There is a possibility for user to use different localized strings, so I have to update it. By using [tableview reloadData]; i'v got the table to update, but the values in NSArray stay the same as they were initialized in first place.
So how to make array look up at the strings once again and get their new values?
Use NSMutableArray instead of NSArray
NSMutableArray (and all other classes with Mutable in the name) can be modified.
You should be using an NSMutableArray. Doing so will allow you to change its values after instantiation.
Your array doesn't need to be mutable here as the array seems to be all or nothing. You dont mention the requirement to delete some objects and not others. NSMutableArray isn't needed. You want to write a lazy loading getter method for the array which reinstantiates it if the array doesnt exist.
-(NSArray *)dataArray{
if (_dataArray){
return _dataArray;
}
_dataArray = NSArray *arrayWithInfo = [[NSArray alloc] initWithObjects:AMLocalizedString(#"Status", nil),AMLocalizedString(#"Call", nil),AMLocalizedString(#"Location", nil),AMLocalizedString(#"Control", nil),AMLocalizedString(#"Sim", nil),AMLocalizedString(#"Object", nil),AMLocalizedString(#"Info", nil),nil];
return _dataArray;
}
Then when you want to reload the tableView
self.dataArray = nil;
[tableView reloadData];
this destroys the old array, forcing it to be remade but with the new localisation.
EDIT:
The issue is the array isn't storing the statement AMLocalizedString(#"Status", nil) its storing the result of that statement, which is the localised string itself. There is no way to make the array re-evaluate that statement without either re-creating the whole array again or using an NSMutableArray and changing all the objects. The lazy loading getter method is more in the objective-c style.
You need to use the NSMutableArray. The NSArray is immutable.

arraycontroller nsmutablearray add programmatically

I trying to add a data on table view via Array Controller thats bind to a NSMutableArray.
On the IB property it looks like this :
and on the code I tried to add the NSMutableArray dynamically then reload the view, bu nothings happened.
for(int i=0;i<10;i++){
NSMutableDictionary *group = [[NSMutableDictionary alloc]init];
[group setValue:[NSString stringWithFormat:#"%#-%d", #"Group", i] forKey:#"groupname"];
[contentArray addObject:group];
}
[tableContent reloadData];
I have been google it and browse the same question in stackoverflow, not found a useful one.
any idea ?
Thanks
updated
I wrote above code in File's owner class.
I think the problem is that the array needs to send a KVO notification to the array controller (or maybe it's the table view, I'm not sure). The way to do that is:
self.contentArray = contentArray; (or _contentArray if that's what your ivar is called). I'm assuming that contentArray is a property, if not, you should make it one.

How can i save objects, but not overwrite in Objective C?

Hello :)
How can i save objects, but not overwrite in Objective C,
i tried NSUserDefaults but when i add object, it will overwrite !
how can i prevent this, because i am trying to create a favorites in my app or bookmarks,
but i don't know how to save those objects i am trying to save.
This is when i am trying to save.
NSUserDefaults *favorite = [NSUserDefaults standardUserDefaults];
[favorite setObject:saveTip forKey:#"saveTipFavorite"];
[favorite synchronize];
But i overwrite every time i push add to favorite button in my app !
And i don't want it to overwrite, but make a new object and append it.
I hope you understand me :)
It is overwriting because there can only be one object per key, and you are changing it each time. What you want to do, is store an array instead of a single object, and set that as a key in your user defaults.
Initialization:
NSMutableArray *objectArray;
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSArray *oldSavedArray = [currentDefaults objectForKey:#"savedArray"];
if (oldSavedArray != nil)
objectArray = [[NSMutableArray alloc] initWithArray:oldSavedArray];
else
objectArray = [[NSMutableArray alloc] init];
Notice we are converting from NSArray to NSMutableArray because the user defaults can only store the immutable (NSArray) version.
Then to add an item:
[objectArray addObject:whatever];
[currentDefaults setObject:objectArray forKey:#"savedArray"];
If this is something you will be doing a lot (tons of data), you may consider using Core Data instead, it will be faster. NSUserDefaults is not really meant for storing heavy data.
The object you save can be an array. Create an array to save the tips and make that the object given to NSUserDefaults.
NSUserDefaults is fine for simple stuff, but if you want your App to handle significant amount of data, you will want to start using CoreData.
http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/cdProgrammingGuide.html
Not only will this solve your saving problem, it will also make it easier to manipulate your data.

Add NSMutableArray to another NSMutableArray

Hey! I've been trying to add an array to an NSMutableArray using the addObject syntax. The app crashes without any explicit mention of an error. Could you please tell me how this is done?
I have a class Stack that creates an array. So I call that class using an instance called tem that I have created. Hence, [self tem] is my call to the array. Through the program I merely add UILabels to the array(I know you'd suggest adding a string and then changing to UILabels, but I need to do it this way) and towards the end, I'd like to add this array to my 'list' array.
-(Stack *)tem {
if(!tem)
tem=[[Stack alloc]init];
return tem;
}
-(void)viewDidLoad{
//code
list=[NSMutableArray arrayWithArray:[self list].array];
}
-(IBAction)opPr:(UIButton *)sender
{
if([[[sender titleLabel]text] compare: #"="]==0) {
//code
UILabel *t=[[UILabel alloc]init];
//complete creating label
[[self tem]push:t];
//add above label to tem array
[list addObject:[self tem].array];
[table reloadData];
}
}
OK, this answer got all cluttered with edits. I've edited it to be more clear, at the possible expense of understanding the thread of how we arrived at the final answer.
The final answer
The answer was to change:
list=[NSMutableArray arrayWithArray:[self list].array];
To:
list = [[NSMutableArray alloc] init];
(...Which isn't really the best way to create an NSMutableArray, but it may work anyway.)
This was based on an erroneous version of the asker's code
Based on your comment that it crashes after [list addObject:[self tem].array];, I must conclude that your instance variable list is of type Stack*, and that Stack is not a subclass of NSMutableArray. If so, that's your issue.
In that case, changing that line to [[list array] addObject:[self tem].array]; should fix it.
This is just good advice
As an aside, NSMutable array is perfectly capable of acting as a stack without modification. Example:
NSMutableArray* ar = [NSMutableArray arrayWithCapacity:someCapacity];
// Push
[ar addObject:narf]; // narf being some object reference
// Pop
id popped = [ar lastObject];
[ar removeLastObject];
If you want to encapsulate stack behavior in a semantically consistent way, you can add push and pop methods to NSMutableArray using a category. This would make your code simpler and less prone to error.
This was my first stab at answering the question, before any code had been posted
This is a stab in the dark since you've not posted any code. BUT. One way to accomplish that with arrays is by creating arrays using [NSArray arrayWithObjects:obj obj ... nil] and omitting the nil terminator on the list of objects. Are you by any chance doing that?