I have a problem with a stored userDefaults array.
I have a simple object (in the image I have cut off some variables) with two strings mutableArray.
When I save this object in the userDefaults I have the same object in the top (of the image), the problem occurs when I retrieve the object; I have the object in the bottom (added by me in a mutableArray, same object before add it).
the problem is that I make comparisons between objects and it returns false because there is an empty NSCFConstantString in the second array.
These are the two methods to store and retrieve the object in the userDefaults:
- (void)saveCustomObject:(NSMutableArray *)object key:(NSString *)key
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:object] forKey:key];
[defaults synchronize];
}
- (NSArray *)loadCustomObjectWithKey:(NSString *)key
{
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:key];
NSMutableArray *objectArray = nil;
if (dataRepresentingSavedArray != nil)
{
NSArray *savedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
if (savedArray != nil)
objectArray = [[NSMutableArray alloc] initWithArray:savedArray];
else
objectArray = [[NSMutableArray alloc] init];
}
return objectArray;
}
I would prefer not to have to write logic to handle this empty string.
Where is it coming from?
Related
I need to update / add a key -> value pair in a NSMutableArray.
I would like to add a key with a fix value if the key isn't set already.
I tried the following - but the app is crashing at addObject with a "mutating method sent to immutable object" error:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *bookmarks = [defaults mutableArrayValueForKey:#"bookmarks"];
for (id bookmark in bookmarks) {
if ([bookmark objectForKey:#"type"] == NULL){
[bookmark addObject:#"old" forKey:#"type"];
}
}
[[NSUserDefaults standardUserDefaults] synchronize];
Your outer array contains immutable dictionaries. One option would be the following:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *bookmarks = [defaults mutableArrayValueForKey:#"bookmarks"];
[booksmarks enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSDictionary *bookmark, NSUInteger index, BOOL *stop) {
if (!bookmark[#"type"]) {
NSMutableDictionary *mutable = [bookmark mutableCopy];
mutable[#"type"] = #"old";
[bookmarks replaceObjectAtIndex:index withObject:[mutable copy]];
}
}];
// Update NSUserDefaults
[[NSUserDefaults standardUserDefaults] setObject:bookmarks forKey:#"bookmarks"];
This should be nice an efficient since it can update multiple dictionaries concurrently.
Try this:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
//SAVING
NSMutableArray *bookmarksSaving = [NSMutableArray new];
[bookmarksSaving addObject:[[NSMutableDictionary alloc] initWithObjects:#[#"red"] forKeys:#[#"color"]]];
[bookmarksSaving addObject:[[NSMutableDictionary alloc] initWithObjects:#[#"yellow"] forKeys:#[#"color"]]];
[bookmarksSaving addObject:[[NSMutableDictionary alloc] initWithObjects:#[#"green",#"new"] forKeys:#[#"color",#"type"]]];
[defaults setObject:bookmarksSaving forKey:#"bookmarks"];
[[NSUserDefaults standardUserDefaults] synchronize];
//MODIFY
NSMutableArray *bookmarks = [defaults mutableArrayValueForKey:#"bookmarks"];
NSUInteger index = 0;
for (NSDictionary *bookmark in bookmarks) {
NSMutableDictionary *bookmarkMutable = [bookmark mutableCopy];
if ([bookmarkMutable objectForKey:#"type"] == nil)
{
[bookmarkMutable setValue:#"old" forKey:#"type"];
[bookmarks replaceObjectAtIndex:index withObject:bookmarkMutable];
}
index++;
}
[[NSUserDefaults standardUserDefaults] synchronize];
You have to save your bookmarks as NSMutableDictionary to be able to add/remove keys/value
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *bookmarks = [defaults objectForKey:#"bookmarks"];
NSMutableArray *newBookMarks = [bookmarks mutableCopy];
for (NSDictionary *bookmark in bookmarks) {
NSMutableDictionary *newBookmark = [bookmark mutableCopy];
if (![bookmark objectForKey:#"type"]){
[newBookmark setObject:#"old" forKey:#"type"];
}
[newBookMarks addObject:newBookmark];
}
[defaults setObject:newBookMarks forKey:#"bookmarks"];
[defaults synchronize];
This question already has answers here:
Attempt to set a non-property-list object as an NSUserDefaults
(12 answers)
Closed 8 years ago.
NSMutableArray *mutableArray = [NSMutableArray array];
Prostate *prostate = [Prostate new];
prostate.prostateCheckTimeString = self.prostateCheckTime.text;
prostate.prostateCubeString =self.prostateCube.text;
prostate.prostateAntigemString =self.prostateAntigem.text;
prostate.peeSpeedMaximumString =self.peeSpeedMaximum.text;
prostate.peeRemenderString =self.peeRemender.text;
[mutableArray addObject:prostate];
NSArray *tempArray = [[NSArray alloc]initWithArray:self.mutableArray];
[Global saveArrayToUserDefault:prostateArray saveValue:tempArray];
NSArray *array = [Global getArrayFromUserDefault:prostateArray];
//Below is my global object
**+(void)saveArrayToUserDefault:(NSString *)userDefaultKey saveValue:(NSArray *)value**
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:value forKey:prostateArray];
[userDefaults synchronize];
}
**+(NSArray *)getArrayFromUserDefault:(NSString *)userDefaultKey**
{
NSArray *temp;
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
temp = [userDefaults objectForKey:userDefaultKey];
return temp;
}
-----------------------------------Description ---------------------------------------
I had Been Search for a while , I know NSUserDefault can only store NSArray , I tried to convert NSMutableArray to NSArray , but still got error
-----------------------------------Error ---------------------------------------------
Property list invalid for format: 200 (property lists cannot contain objects of type 'CFType') Attempt to set a non-property-list object (
""
Implement the <NSCoding> protocol in your entity class which you wanna save
-(void)writeArrayWithCustomObjToUserDefaults:(NSString *)keyName withArray:(NSMutableArray *)myArray {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:myArray];
[defaults setObject:data forKey:keyName];
[defaults synchronize]; }
-(NSArray *)readArrayWithCustomObjFromUserDefaults:(NSString*)keyName {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:keyName];
NSArray *myArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
[defaults synchronize];
return myArray; }
Prostate is your custom object and NSUserDefaults does not allow you to save it.
So you have to Archive it before saving to NSUserDefaults and Unarchive it when reading back from NSUserDefaults.
So your methods to save and read user defaults data should look like:
-(void)writeArrayWithCustomObjToUserDefaults:(NSString *)keyName withArray:(NSMutableArray *)myArray {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:myArray];
[defaults setObject:data forKey:keyName];
[defaults synchronize];
}
-(NSArray *)readArrayWithCustomObjFromUserDefaults:(NSString*)keyName {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:keyName];
NSArray *myArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
return myArray;
}
I managed to save an NSMutableArray in NSUserDefaults by first converting it to NSData - I dont deal with a lot of data and just want the data to be there after I switch off & on my phone - but the data does not show up in my table where I would display it. I write the NSUserDefaults back to my array upon loading. Maybe one of you has a hint...? Below the button action where I write to NSUserDefaults and the method viewDidLoad where I write NSUserDefaults to my original array (toDoitems)
- (IBAction)unwindToList:(UIStoryboardSegue *)segue
{
XYZAddToDoItemViewController *source = [segue sourceViewController];
XYZToDoItem *item = source.toDoItem;
if (item !=nil) {
[self.toDoitems addObject:item];
NSString *error;
NSData *data = [NSPropertyListSerialization dataFromPropertyList:self.toDoitems format:NSPropertyListBinaryFormat_v1_0 errorDescription:&error];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"itemArray"];
[[NSUserDefaults standardUserDefaults] synchronize];
[self.tableView reloadData];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.toDoitems = [[NSMutableArray alloc] init];
self.toDoitems = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"itemArray"]];
}
Heres one way to do this
Add encoder decoder functions to your XYZToDoItem class
Something like this if say you had 2 strings in this class string1 and string2 :
(i havent compiled this code but you get the idea)
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.string1 forKey:#"string1"];
[aCoder encodeObject:self.string2 forKey:#"string2"];
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self)
{
self.string1 = [aDecoder decodeObjectForKey:#"string1"];
self.string2 = [aDecoder decodeObjectForKey:#"string2"];
}
return self;
}
Then when you are ready to save do the following
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.myDataArray];
[userDefaults setObject:data forKey:#"storageName"];
// To load the data from NSUserDefaults
NSData *myData = [[NSUserDefaults standardUserDefaults] objectForKey:#"storageName"];
NSArray *temp = (NSMutableArray*)[NSKeyedUnarchiver unarchiveObjectWithData:myData];
self.myDataArray = (NSMutableArray*)[temp mutableCopy];
I have a tableview that show saved data, but when I try to go from selected cell, it doesn't show the data that's been saved. It try this
I used NSUserDefaults to save data in other view (another .m file)
- (IBAction)saveCourseDetail:(id)sender
{
NSMutableDictionary *courseDictionary=[NSMutableDictionary new];
[courseDictionary setObject:courseName.text forKey:#"courseName"];
[courseDictionary setObject:courseDescription.text forKey:#"courseDescription"];
[courseDictionary setObject:classRoom.text forKey:#"classRoom"];
[courseDictionary setObject:teacherName.text forKey:#"teacherName"];
[courseDictionary setObject:buildingName.text forKey:#"buildingName"];
[globArray addObject:courseDictionary];
NSUserDefaults *savedData=[NSUserDefaults standardUserDefaults];
[savedData setObject:globArray forKey:#"array"];
// [savedData setObject:courseDictionary forKey:#"courseDictionary"];
[savedData synchronize];
[[NSNotificationCenter defaultCenter] postNotificationName:#"CourseAddedNotification" object:nil];
[self.navigationController popViewControllerAnimated:YES];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
CourseDetailViewController *courseDetails=[[CourseDetailViewController alloc]init] ;
courseDetails.savedDataDic = [globArray objectAtIndex:indexPath.row];
[self.navigationController pushViewController: courseDetails animated:YES];
}
My problem is that I cant go from a selected cell to the view that the data's been saved.
I personally recommend you to use core data if there is large amount of data to be stored. Using user defaults is not good to store large chunks of data. Anyways, if you want to store an array of dictionary, you have to encode it and to read the array you have to decode it like this
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *arr = ... ; // set value
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arr];
[defaults setObject:data forKey:#"theKey"];
Load:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:#"theKey"];
NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:data];
The element in the array implements
#interface DictionaryItems : 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;
}
Hope it helps :)
When I use this method first time it works fine, but when I called it second time I get the error "mutating method sent to immutable object". The problem is at line with "addObject" command.
-(IBAction) save: (id) sender{
NSMutableArray *placesT= [[NSUserDefaults standardUserDefaults] objectForKey:#"placesT"];
if (!placesT) {
placesT=[[[NSMutableArray alloc] init] autorelease];
}
[placesT addObject: [NSString stringWithFormat:#"%#", tagF.text] ];
NSUserDefaults *tUD=[NSUserDefaults standardUserDefaults];
[tUD setObject:placesT forKey:#"placesT"];
[tUD synchronize];
[self dismissModalViewControllerAnimated:YES];
}
As the documentation for NSUserDefaults says: "Values returned from NSUserDefaults are immutable, even if you set a mutable object as the value." Whenever you want to change a collection you get from NSUserDefaults you have to get the immutable version, make a mutableCopy, modify that, and set it back again.
That is because the object stored in the NSUserDefaults is not the mutableArray but a normal array.
- (IBAction)save:(id)sender {
NSMutableArray *placesT = nil;
NSArray *tempArray = [[NSUserDefaults standardUserDefaults] objectForKey:#"placesT"];
if (tempArray) {
placesT = [tempArray mutableCopy];
} else {
placesT = [[NSMutableArray alloc] init];
}
[placesT addObject:[NSString stringWithFormat:#"%#", tagF.text]];
NSUserDefaults *tUD = [NSUserDefaults standardUserDefaults];
[tUD setObject:placesT forKey:#"placesT"];
[tUD synchronize];
[self dismissModalViewControllerAnimated:YES];
[placesT release];
}
placesT is a non mutable array, either always set placesT a mutable object always or use following code.
NSMutableArray *placesT= [[[NSUserDefaults standardUserDefaults] objectForKey:#"placesT"] mutableCopy];
This should work:
-(IBAction) save: (id) sender {
NSMutableArray *placesT= [[NSMutableArray alloc]initWithArray:[[NSUserDefaults standardUserDefaults]
objectForKey:#"placesT"]];
if (!placesT) {
placesT=[[[NSMutableArray alloc] init] autorelease];
}
[placesT addObject: [NSString stringWithFormat:#"%#", tagF.text] ];
NSUserDefaults *tUD=[NSUserDefaults standardUserDefaults];
[tUD setObject:placesT forKey:#"placesT"];
[tUD synchronize];
[self dismissModalViewControllerAnimated:YES]; }