NSMutableArray in NSUserDefaults - iphone

I'm sorry for my bad english but i'm trying to explain with best words.
I have some problem when i'm trying to insert an NSMutableArray in NSUserDefaults ([NSUserDefaults setObject:forKey:]: Attempt to insert non-property value ')
My code to insert the array is as follows:
-(void)saveToUserDefaults:(NSMutableArray*)myArray
{
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSArray *array = [[NSArray alloc ] initWithArray:myArray];
if (standardUserDefaults)
{
[standardUserDefaults setObject:array forKey:#"MyArray"];
[standardUserDefaults synchronize];
}
}
My code to retrieve the array:
-(NSMutableArray*)retrieveFromUserDefaults
{
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *val = [[NSMutableArray alloc]init];
if (standardUserDefaults)
val = [NSMutableArray arrayWithArray:[standardUserDefaults arrayForKey:#"MyArray"]];
return val;
}
and in my code :
NSMutableArray :
series = [[NSMutableArray alloc]initWithObjects:nil];
//add some object inf my array...
Save my NSMutableArray :
[self saveToUserDefaults:series];
Retrieve my NSMutableArray :
series = [self retrieveFromUserDefaults];
I think it's not the best way to do this, so if anyone have ideas , it'll be helpful for me.
Thanks for reading.
Tommy

Only immutable NSArrays can be placed into defaults. Rather than placing a NSMutableArray there, convert to regular array using [NSArray arrayWithArray:] and place that one into defaults.
For retrieval, retrieve an NSArray and then use [NSMutableArray arrayWithArray:].

You can use following method to get the mutable object from immutable. It's not optimized and only implemented for NSArray and NSDictionary.
+ (id) GetMutable:(id)input {
id result;
if ([input superclass] == [NSArray class] || [input superclass] == [NSMutableArray class]) {
result = [[NSMutableArray alloc] initWithArray:input copyItems:YES];
for (int i = 0; i < [(NSMutableArray*)result count]; i++) {
[(NSMutableArray*)result replaceObjectAtIndex:i withObject:[Globals GetMutable:[(NSMutableArray*)result objectAtIndex:i]]];
}
} else if ([input superclass] == [NSDictionary class] || [input superclass] == [NSMutableDictionary class]) {
result = [[NSMutableDictionary alloc] initWithDictionary:input copyItems:YES];
NSArray *keys=[(NSMutableDictionary*)result allKeys];
for (int i = 0; i < keys.count; i++) {
[(NSMutableDictionary*)result setObject:[Globals GetMutable:[(NSMutableDictionary*)result objectForKey:[keys objectAtIndex:i]]] forKey:[keys objectAtIndex:i]];
}
}
else {
return input;
}
return result;
}

I hope this tutorial helps, who expect answer for this question.
-(void)saveToUserDefaults:(NSString*)myServerName uname:(NSString*)myUserName
pass:(NSString*)myPassword
{
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
if (standardUserDefaults) {
[standardUserDefaults setObject:myServerName forKey:#"ServerKey"];
[standardUserDefaults setObject:myUserName forKey:#"UserNameKey"];
[standardUserDefaults setObject:myPassword forKey:#"PasswordKey"];
[standardUserDefaults synchronize];
}
}
-(NSArray*)retrieveFromUserDefaults
{
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSString *serverName = nil;
NSString *userName = nil;
NSString *password = nil;
if (standardUserDefaults)
serverName = [standardUserDefaults objectForKey:#"ServerKey"];
userName = [standardUserDefaults objectForKey:#"UserNameKey"];
password = [standardUserDefaults objectForKey:#"PasswordKey"];
NSArray* credentials = [NSArray arrayWithObjects:serverName,userName,password, nil];
return credentials;
}
To Pass values
[self saveToUserDefaults:serverVariable uname:usernameVariable pass:passVariable];
To get Values
NSArray *result=[self retrieveFromUserDefaults];
Happy coding!!!

The contents of your array can only by plist valid objects: (NSString, NSNumber, NSDate, NSData, NSArray, or NSDictionary objects).
See documentation for NSUserDefaults setObject:forKey: and What is a Property List?

Your code needs a lot of clean up. Here is the simple and correct way to access standard user defaults:
The value you pass to your save method does not need to be mutable, although it can be. But since you are not mutating it inside your method, there's no need for it to be mutable. It just has to be not nil, which you'll check before saving:
-(void)saveToUserDefaults:(NSArray*)myArray
{
if (myArray) {
NSUserDefaults *myDefaults = [NSUserDefaults standardUserDefaults];
myDefaults[#"myArray"] = myArray;
}
}
Standard user defaults only returns non-mutable objects, which you can convert to a mutable copy using the "mutableCopy" method:
-(NSMutableArray*)retrieveFromUserDefaults
{
NSUserDefaults *myDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *val = [[NSMutableArray alloc] init];
val = [myDefaults[#"MyArray"] mutableCopy];
return val;
}

Related

Storing Multiple value in MutableArray with different value

I have a button which on click I store the index of the current quotes into an array.
The problem is that it is overwriting the same object. How can I change it with different object of the NSNumber to store in the NSMutableArray?
[someButton setImage:[UIImage imageNamed:#"add_favoritegold.png"] forState:UIControlStateHighlighted];
NSUserDefaults *favoriteQuotes = [NSUserDefaults standardUserDefaults];
NSMutableArray *ListOfIndex= [[NSMutableArray alloc] init];
//Want to change with diffrent objecy
**NSNumber* Wrapped = [NSNumber numberWithInt:index];**
[ListOfIndex addObject:Wrapped];
int xOut = [[ListOfIndex lastObject] intValue];
NSLog(#"%d",xOut);
[favoriteQuotes setObject:ListOfIndex forKey:#"Indexes"];
Try this
- (void)saveFavouriteQuoteIndex:(NSUInteger)index
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *savedQuoteIndexes = [[defaults objectForKey:#"Indexes"]mutableCopy];
if (!savedQuoteIndexes) {
savedQuoteIndexes = [NSMutableArray array];
}
NSNumber *indexNumber = #(index);
if (![savedQuoteIndexes containsObject:indexNumber]) {
[savedQuoteIndexes addObject:indexNumber];
[defaults setObject:savedQuoteIndexes forKey:#"Indexes"];
[defaults synchronize];
}
}
You are saving ListOfIndex with key as Indexes.
So, for the next whatever value, it will replace previous value having key Indexes.
EDIT:
Read the array
NSArray *readArray=favoriteQuotes[#"Indexes"];
Add your new value into this same array.

how to check a mutable string contains a string value or not in iphone?

I have beeen storing some string values to an mutablearray to store in to a nsuserdefaults.like the way `
NSMutableArray *arrayObj = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"emoved"]];
for(int i = 0 ; i<[searcharray count]; i++)
{
NSLog(#"%#",searcharray);
NSDictionary *dictionarydate = [searcharray objectAtIndex:i];
NSString *memeid=[[dictionarydate objectForKey:#"ID"]description];
if([dictionarydate objectForKey:#"dateOfInfo"])
{
if ([arrayObj containsObject:memeid])
{
}
else
{
[arrayObj addObject:memeid];
}
}
}
[[NSUserDefaults standardUserDefaults] setObject:arrayObj forKey:#"emoved"];
` so when printng you need to add a value only at once .but when i am priting that value i am seing the same value is added to the array multiple times.is there anything wrong in my approach can anybody point out?
You should allocate and initialize your NSMutableArray like
NSMutableArray *arrayObj = [[NSMutableArray alloc] initWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"emoved"]];
If you aren't using ARC, don't forget to release your arrayObj right after you're done with it

find special keys in UserDefaults by "substringToIndex"

I'm grouping my UserDefault keys by specific prefixes.
e.g.
[NSUserDefaults standardUserDefaults] setInteger: 1 forKey: #"prefix1_someText_Key"]
[NSUserDefaults standardUserDefaults] setInteger: 2 forKey: #"prefix2_someText_Key"]
[NSUserDefaults standardUserDefaults] setInteger: 3 forKey: #"prefix4_someText_Key"]
//.....
Now, I'd like to find all the keys, that start with e.g. "prefix", and load them into an array. is there a way for doing that (programmatically)?
You could use the underlying NSDictionary to find the suitable keys:
NSString *myPrefix = #"prefix";
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *dict = [defaults dictionaryRepresentation];
NSMutableArray *keysWithPrefix = [NSMutableArray array]
for (NSString *key in dict.keyEnumerator) {
if ([key hasPrefix: myPrefix]) {
[keysWithPrefix addObject: key];
}
}
// now keysWithPrefix contains all matching keys
UPDATE
For debugging reasons you could add a log to see what keys are being dropped:
for (NSString *key in dict.keyEnumerator) {
if ([key hasPrefix: myPrefix]) {
[keysWithPrefix addObject: key];
} else {
NSLog(#"Dropping key %#", key);
}
}

Why is NSUserDefaults unwilling to save my NSDictionary?

I'm using KVC to serialize an NSObject and attempt to save it to NSUserDefaults, which is giving me an Attempt to insert non-property value when I try to store my NSDictionary.
Following are the properties of the object in question, MyClass:
#interface MyClass : NSObject
#property (copy,nonatomic) NSNumber* value1;
#property (copy,nonatomic) NSNumber* value2;
#property (copy,nonatomic) NSString* value3;
#property (copy,nonatomic) NSString* value4;
#property (copy,nonatomic) NSString* value5;
#property (copy,nonatomic) NSString* value6;
#property (copy,nonatomic) NSString* value7;
#property (copy,nonatomic) NSString* value8;
#end
When it is time to save MyClass it occurs here:
-(void)saveMyClass
{
NSArray* keys = [NSArray arrayWithObjects:
#"value1",
#"value2",
#"value3",
#"value4",
#"value5",
#"value6",
#"value7",
#"value8",
nil];
NSDictionary* dict = [self dictionaryWithValuesForKeys:keys];
for( id key in [dict allKeys] )
{
NSLog(#"%# %#",[key class],[[dict objectForKey:key] class]);
}
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:dict forKey:[NSString stringWithString:kMyClassKey]];
[defaults synchronize];
}
which produces this output:
2012-02-23 19:35:27.518 MyApp[10230:40b] __NSCFConstantString __NSCFNumber
2012-02-23 19:35:27.519 MyApp[10230:40b] __NSCFConstantString __NSCFNumber
2012-02-23 19:35:27.519 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.519 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString __NSCFString
2012-02-23 19:35:27.520 MyApp[10230:40b] __NSCFConstantString NSNull
2012-02-23 18:38:48.489 MyApp[9709:40b] *** -[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value '{
value1 = "http://www.google.com";
value2 = "MyClassData";
value3 = 8;
value4 = "<null>";
value5 = "http://www.google.com";
value6 = 1;
value7 = "http://www.google.com";
value8 = 4SY8KcTSGeKuKs7s;
}' of class '__NSCFDictionary'. Note that dictionaries and arrays in property lists must also contain only property values.`
As you can see, all of the objects in the dict are property list values and all of its keys are NSString*. What trivia am I lacking in order to execute this? Or should I give up and use writeToFile or similar?
Props to Kevin who suggested printing the values, of course one of which is of type NSNull which is not a property list value. Thanks!
The kludgy solution to my problem - iterate over the keys of the dictionary I just produced so conveniently with dictionaryWithValuesForKeys and remove those of type NSNull. sigh
NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithDictionary:[self dictionaryWithValuesForKeys:keys]];
for( id key in [dict allKeys] )
{
if( [[dict valueForKey:key] isKindOfClass:[NSNull class]] )
{
// doesn't work - values that are entered will never be removed from NSUserDefaults
//[dict removeObjectForKey:key];
[dict setObject#"" forKey:key];
}
}
I usually archive and unarchive dictionaries when saving them to the user defaults.
This way you don't have to manually check for NSNull values.
Just add the following two methods to your code. Potentially in a category.
- (BOOL)archive:(NSDictionary *)dict withKey:(NSString *)key {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = nil;
if (dict) {
data = [NSKeyedArchiver archivedDataWithRootObject:dict];
}
[defaults setObject:data forKey:key];
return [defaults synchronize];
}
- (NSDictionary *)unarchiveForKey:(NSString *)key {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:key];
NSDictionary *userDict = nil;
if (data) {
userDict = [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
return userDict;
}
Then you can archive any dictionary like this (assuming the method are available in the class):
NSDictionary *dict = ...;
[self archive:dict withKey:#"a key of your choice"];
and retrieve it later on again like this:
NSDictionary *dict = [self unarchiveForKey:#"a key of your choice"];
If you need to store;
data from a custom object,
or an array of custom objects
you can use NSKeyedArchiver methods. You can check leviathan's answer for this method.
However, if you are trying to store;
a dictionary that contains either NSString or NSNumber (like a dictionary converted from a JSON service response),
or array of this kind of dictionary
you don't need to use NSKeyedArchiver. You can use user defaults.
In my case, when I retrieve the dictionary from user defaults it was returning nil, so I thought NSUserDefaults is unwilling to save my dictionary.
However, it was saved, but I was using the wrong getter method to retrieve it from user defaults;
[[NSUserDefaults standardUserDefaults] stringForKey:<my_key>]
Please make sure you used either;
[[NSUserDefaults standardUserDefaults] dictionaryForKey:<my_key>]
or;
[[NSUserDefaults standardUserDefaults] objectForKey:<my_key>]
before checking any other possible reason.

iPhone: preserve NSUserDefaults values when application is killed

I am trying to implement "Add to Favorites" functionality using NSUserDefaults. So far I have written following code.
- (void)favouriteButtonClicked:(id)sender
{
favselected = !favselected; // favselected is BOOL property
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSString* viewname = #"custom";
if(favselected) {
[favButton setBackgroundImage:[UIImage imageNamed:#"selected.png"] forState:UIControlStateNormal];
[appDelegate addTOFavourites:self.ViewID viewType:self.ViewType];
} else {
[favButton setBackgroundImage:[UIImage imageNamed:#"unselected.png"] forState:UIControlStateNormal];
[appDelegate removeFromFavourites:self.ViewID viewType:self.ViewType];
}
}
It is working fine as long as my application is running but when I killed my application, I am losing all my stored values so when next time view loaded, in viewload isAddedToFavorites method returns false. Is there anyway to preserve my values? Am I missing something?
if([appDelegate isAddedToFavorites:self.ViewID]) {
[favButton setBackgroundImage:[UIImage imageNamed:#"selected.png"] forState:UIControlStateNormal];
favselected = YES;
} else {
[favButton setBackgroundImage:[UIImage imageNamed:#"unselected.png"] forState:UIControlStateNormal];
favselected = NO;
}
Edit:
I tried using NSMutableDictionary as I have to add multiple key-values but following method always display Count=0 even after adding object to dictionary. Any help would be really appreciated. Thank you.
-(BOOL)isAddedToFavorites:(NSString*)viewID {
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *favourites = [[standardUserDefaults objectForKey:kFavouriteItemsKey] mutableCopy];
if(favourites && [[favourites objectForKey:kFavouriteItemsKey] objectForKey:viewID])
return YES;
return NO;
}
-(void)addToFavourites:(NSString*)viewID viewType:(NSString*)viewType {
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *dict = [[standardUserDefaults objectForKey:kFavouriteItemsKey] mutableCopy];
if(standardUserDefaults) {
if(![dict objectForKey:viewID])
[dict setObject:viewType forKey:viewID]; // It is coming here but still count zero!
NSLog(#"count = %d", [dict count]);
[standardUserDefaults setObject:dict forKey:kFavouriteItemsKey]; // Always dict remains null with 0 objects in it
[standardUserDefaults synchronize];
[dict release];
}
}
-(void)removeFromFavourites:(NSString*)viewID viewType:(NSString*)viewType {
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *dict = [[standardUserDefaults objectForKey:kFavouriteItemsKey] mutableCopy];
if(standardUserDefaults) {
if ([dict objectForKey:viewID])
[dict removeObjectForKey:viewID];
[standardUserDefaults setObject:dict forKey:kFavouriteItemsKey];
[standardUserDefaults synchronize];
[dict release];
}
}
Thanks.
NSUserDefaults is actually used to store values permanently, in fact if you create any Settings for your program they will be saved as NSUserDefaults.
I think the problem is that you are not saving it with the same key you are retrieving. Try saving like this:
//To save
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:appDefaults forKey:kFavouriteItemsKey];
[[NSUserDefaults standardUserDefaults] synchronize];
//To retrieve
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *favourites = [[standardUserDefaults objectForKey:kFavouriteItemsKey] mutableCopy];
For the dictionary try:
NSDictionary *myDictionary= [[NSUserDefaults standardUserDefaults] objectForKey:kFavouriteItemsKey];
// Create a mutable dictionary to replace the old immutable dictionary
NSMutableDictionary *myMutableDictionary= [NSMutableDictionary dictionaryWithCapacity:[myDictionary count]+1];
// transfer the old dictionary into the new dictionary
[myMutableDictionaryaddEntriesFromDictionary:myDictionary];
// Now add the data
[myMutableDictionary setObject:myObject forKey:myKey];