Why NSUserDefaults failed to save NSMutableDictionary in iOS?

IosObjective CCocoa TouchNsuserdefaultsNscoding

Ios Problem Overview


I'd like to save an NSMutableDictionary object in NSUserDefaults. The key type in NSMutableDictionary is NSString, the value type is NSArray, which contains a list of object which implements NSCoding. Per document, NSString and NSArray both are conform to NSCoding.

I am getting this error:

> [NSUserDefaults setObject: forKey:]: Attempt to insert non-property value.... of class NSCFDictionary.

Ios Solutions


Solution 1 - Ios

I found out one alternative, before save, I encode the root object (NSArray object) using NSKeyedArchiver, which ends with NSData. Then use UserDefaults save the NSData.

When I need the data, I read out the NSData, and use NSKeyedUnarchiver to convert NSData back to the object.

It is a little cumbersome, because i need to convert to/from NSData everytime, but it just works.

Here is one example per request:

Save:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *arr = ... ; // set value
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
[defaults setObject:data forKey:@"theKey"];
[defaults synchronize];

Load:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:@"theKey"];
NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];

The element in the array implements

@interface CommentItem : NSObject<NSCoding> {
    NSString *value;
}

Then in the implementation of CommentItem, provides two methods:

-(void)encodeWithCoder:(NSCoder *)encoder
{
	[encoder encodeObject:value forKey:@"Value"];
}

-(id)initWithCoder:(NSCoder *)decoder
{
	self.value = [decoder decodeObjectForKey:@"Value"];
    return self;
}

Anyone has better solution?

Thanks everyone.

Solution 2 - Ios

If you're saving an object in user defaults, all objects, recursively, all the way down, must be property list objects. Conforming to NSCoding doesn't mean anything here-- NSUserDefaults won't automatically encode them into NSData, you have to do that yourself. If your "list of object which implements NSCoding" means objects that are not property list objects, then you'll have to do something with them before saving to user defaults.

FYI the property list classes are NSDictionary, NSArray, NSString, NSDate, NSData, and NSNumber. You can write mutable subclasses (like NSMutableDictionary) to user preferences but the objects you read out will always be immutable.

Solution 3 - Ios

Are all of your keys in the dictionary NSStrings? I think they have to be in order to save the dictionary to a property list.

Solution 4 - Ios

Simplest Answer :

NSDictionary is only a plist object , if the keys are NSStrings. So, Store the "Key" as NSString with stringWithFormat.


Solution :

NSString *key = [NSString stringWithFormat:@"%@",[dictionary valueForKey:@"Key"]];

Benefits :

  1. It will add String-Value.
  2. It will add Empty-Value when your Value of Variable is NULL.

Solution 5 - Ios

Have you considered looking at implementing the NSCoding Protocol? This will allow you encode and decode on the iPhone with two simple methods that are implemented with the NSCoding. First you would need to adding the NSCoding to your Class.

Here is an example:

This is in the .h file

@interface GameContent : NSObject <NSCoding>

Then you will need to implement two methods of the NSCoding Protocol.

    - (id) initWithCoder: (NSCoder *)coder
    {
    	if (self = [super init])
    	{
               [self setFoundHotSpots:[coder decodeObjectForKey:@"foundHotSpots"]];
        }
        return self;
    }
    
    - (void) encodeWithCoder: (NSCoder *)coder
    {
           [coder encodeObject:foundHotSpots forKey:@"foundHotSpots"];
    }

Check out the documentation on NSCoder for more information. That has come in really handy for my projects where I need to save the state of the application on the iPhone if the application is closed and restore it back to it's state when its back on.

The key is to add the protocol to the interface and then implement the two methods that are part of NSCoding.

I hope this helps!

Solution 6 - Ios

There is no better solution. Another option would be to just save the coded object to disk - but that is doing the same thing. They both end up with NSData that gets decoded when you want it back.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBlueDolphinView Question on Stackoverflow
Solution 1 - IosBlueDolphinView Answer on Stackoverflow
Solution 2 - IosTom HarringtonView Answer on Stackoverflow
Solution 3 - IosSophie AlpertView Answer on Stackoverflow
Solution 4 - IosBhavinView Answer on Stackoverflow
Solution 5 - IosNiels HansenView Answer on Stackoverflow
Solution 6 - IosDan KeenView Answer on Stackoverflow