Why NSUserDefaults failed to save NSMutableDictionary in 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 {
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.
Why does NSUserDefaults fail to save NSMutableDictionary?
From Apple's documentation for NSUserDefaults objectForKey
:
The returned object is immutable, even if the value you originally set was mutable.
The line:
dictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
discards the previously created NSMutableDictionary
and returns a NSDictionary
.
Change the loading to:
NSData *data = [[NSUserDefaults standardUserDefaults]objectForKey:@"Key"];
dictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
Complete example, there is also no need to use NSKeyedArchiver
in this example:
NSDictionary *firstDictionary = @{@"Key 4":@4};
[[NSUserDefaults standardUserDefaults] setObject:firstDictionary forKey:@"Key"];
NSMutableDictionary *dictionary = [[[NSUserDefaults standardUserDefaults] objectForKey:@"Key"] mutableCopy];
dictionary[@"Key 1"] = @0;
dictionary[@"Key 2"] = @1;
dictionary[@"Key 3"] = @2;
for (NSString * key in [dictionary allKeys]) {
NSLog(@"key: %@, value: %@", key, [dictionary objectForKey:key]);
}
NSLog output:
key: Key 2, value: 1
key: Key 1, value: 0
key: Key 4, value: 4
key: Key 3, value: 2
Why can't I store NSDictionary in NSUserDefaults?
NSDictionary
is a property list object. But in order to store a dictionary, all keys must be NSString
and all values must also be property list objects.
Your dictionary contains a non-property list object for the key profile_image
since its value is NSNull
.
Remove that value from the dictionary and your code should work. Of course your code that loads the dictionary back must deal with the possibility that the key won't be there indicating that it was "null".
Since you are working with NSJSONSerialization
, it's possible you could have several NSNull
values in the resulting data. You need to recursively find and remove any and all NSNull
values.
Why is NSUserDefaults unwilling to save my NSDictionary?
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];
}
}
NSUserDefaults fail to save NSMutableDictionary
There are very specific restrictions as to what type of data can be synced to NSUserDefaults
within an NSDictionary
. These include NSString, NSNumber, NSData, NSArray and NSDictionary
. Use your NSCoder
methods to serialize to NSData
(using NSKeyedArchiver
) before storing in the NSUserDefaults
.
Also, take a look at this
iPhone: Not able to store NSMutableDictionary to NSUserDefaults
You will need to implement this for the elements in your dictionary:
The element in the dictionary implements
@interface CommentItem : NSObject {
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;
}
As suggested on the link @Irene provided.
NSMutableDictionary and NSUserDefaults
Of course, only the last valeur is save, as you don't load the previous value in weightLog before adding the new one ! Also one more things, you should change the key for each value.
Try to use this (the date will include year + month + day + hour + minute + second, so don't click more than 1 time per second ;-D ) :
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *weightLog = [NSMutableDictionary dictionaryWithDictionary:[userDefaults objectForKey:@"Weightlog"]];
NSString *date = [NSDateFormatter localizedStringFromDate:[NSDate date]
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterFullStyle];
[weightLog setObject:self.weightInput.text forKey:date];
NSLog(@"neweight you entered is %@", self.weightInput.text);
NSLog(@"weightlog is %@", weightLog);
[userDefaults setObject:weightLog forKey:@"Weightlog"];
[userDefaults synchronize];
Related Topics
How to Tell Swiftui Views to Bind to Nested Observableobjects
In Swift, How to Declare a Variable of a Specific Type That Conforms to One or More Protocols
Swiftui App Life Cycle Ios14 Where to Put Appdelegate Code
Uibutton Action in Table View Cell
How to Run App in Simulator: Xcode Beta 6 iOS 8
Uiscrollview Pauses Nstimer Until Scrolling Finishes
Trying to Handle "Back" Navigation Button Action in Ios
Nslog on Devices in iOS 10/Xcode 8 Seems to Truncate - Why
Uiwebview: Html5 Audio Pauses in iOS 6 When App Enters Background
Sending Data With Segue With Swift
Detecting the Call Events in Ios
#Warning: C-Style For Statement Is Deprecated and Will Be Removed in a Future Version of Swift
Anyobject Not Working in Xcode8 Beta6
Firebasestorage: How to Delete Directory
How to Handle Different Orientations in Ios
How to Add an In-App Purchase to an iOS Application