NSMutableArray to NSUserDefults not saving - objective-c

I am reading and writing an array to NSuserdefults in this way:
indexDelete = button.tag;
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:#"save"];
if (dataRepresentingSavedArray != nil)
{
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
if (oldSavedArray != nil)
myIntegers = [[NSMutableArray alloc] initWithArray:oldSavedArray];
else
myIntegers = [[NSMutableArray alloc] init];
}
[myIntegers addObject:[NSNumber numberWithInteger:indexDelete]];
NSLog (#"Array myIntegers: %#", myIntegers);
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:myIntegers] forKey:#"save"];
Every thing works fine if I keep the line:
myIntegers = [[NSMutableArray alloc] init];
out of the brackets, because even if the data is there, it is not written or read correctly:
I get this error: -[NSKeyedUnarchiver initForReadingWithData:]: data is NULL
What im I doing wrong??

Change this:
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:myIntegers] forKey:#"save"];
to
[[NSUserDefaults standardUserDefaults] setObject:myIntegers forKey:#"save"];
if myIntegers will not be mutated (changed) in the future. If it will be changed then use:
[[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithArray:myIntegers] forKey:#"save"];
The ability to save arrays and dictionaries in the user defaults is documented in the class description.

You never initialize your myIntegers. The first time you get the object for key save from your defaults you get a nil object (hence not entering the if closure and allocating the structure). After that you try to archive a nil object, which is probably the cause for the error you're getting.
Try moving the initialization of the array before this and only adding the objects from the previous saving if they exist:
myIntegers = [[NSMutableArray alloc] init];
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:#"save"];
if (dataRepresentingSavedArray != nil)
{
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
if (oldSavedArray != nil)
[myIntegers addObjectsFromArray:oldSavedArray];
}

I have one alternative to do this is to save mutablearray in doc dir and then retrieve it:
// Get path to documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
if ([paths count] > 0)
{
// Path to save array data
NSString *arrayPath = [[paths objectAtIndex:0]
stringByAppendingPathComponent:#"array.out"];
// Write array
[yourMutableArray writeToFile:arrayPath atomically:YES];
// Read both back in new collections
NSMutableArray *arrayFromFile = [NSMutableArray arrayWithContentsOfFile:arrayPath];
for (NSString *element in arrayFromFile)
NSLog(#"Items: %#", element);
}

Related

Getting thread 1 signal SIGQUIT

I am getting thread 1: signal SIGQUIT when running my app. But when I click on continue execution, app is resuming and completing the task. Not sure why I am getting this
NSArray *keyNamesForKAV = [[NSArray alloc]initWithObjects:#"aKAV",#"bKAV",#"cKAV",#"dKAV",#"eKAV",#"fKAV",#"gKAV",#"hKAV",#"iKAV",#"jKAV",#"kKAV",#"lKAV",#"mKAV",#"nKAV",#"oKAV",#"pKAV",#"qKAV",#"rKAV",#"sKAV",#"tKAV",#"uKAV",#"vKAV",#"wKAV",#"xKAV",#"yKAV",#"zKAV", nil];
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
KAVTrie *kavTree = [defaults rm_customObjectForKey:#"zKAV"];
NSMutableArray *KAVArray = [[NSMutableArray alloc]init];
if (!kavTree){
for (int i = 0 ; i < 26; i++){
KAVTrie *trie = [[KAVTrie alloc]init];
KAVTrie *trieLoad = [[KAVTrie alloc]init];
NSString *path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"%c",[[keyNamesForKAV objectAtIndex:i] characterAtIndex:0]] ofType:#"plist"];
NSDictionary *tempDict = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray *array = [tempDict allKeys];
trie = [trieLoad storeTrieInArray:array];
[KAVArray addObject:trie];
}
NSLog(#"KAV objects created");
for (int i = 0 ; i < 26; i++){
[defaults rm_setCustomObject:[KAVArray objectAtIndex:i] forKey:[keyNamesForKAV objectAtIndex:i]];
[defaults synchronize];
}
NSLog(#"KAV objects loaded in user defaults");
}
I am getting error in the second for loop
I figured out. There is an issue with my KAV class file

insert object in an NSMutableArray with NSmutableDictionary in UserDefaults

I have an NSMutableArray. The array is storing NSMutableDictionary. The dictionary object is storing NSUserDefault values. These are slider values previously selected by user. Now I want that, when user move these sliders then new slider values must be saved in NSUserDefaults.
I'm using this method to add a value in the NSMutableArray
NSMutableArray *quarterValueArray = [[[NSUserDefaults standardUserDefaults]arrayForKey:#"QuarterValues"] mutableCopy];
if (!quarterValueArray)
{
NSMutableArray *aQuarterArray = [[NSMutableArray alloc] init];
for (int i = 0; i < 12; i++)
{
NSMutableDictionary *dict1 = [[[NSMutableDictionary alloc] init] mutableCopy];
[dict1 setObject:#"" forKey:#"quarterP"];
[dict1 setObject:#"" forKey:#"quartersliderValue"];
[dict1 setObject:#"" forKey:#"totalOfTheQuater"];
[aQuarterArray addObject:dict1];
}
[[NSUserDefaults standardUserDefaults] setObject:aQuarterArray forKey:#"QuarterValues"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
and then for storing slider values i am using this method in sliderviewCntroller
-(void)saveUserDefaultDataQuarter
{
NSMutableDictionary *quarter = [QuarterValueArray objectAtIndex:0];
[quarter setObject:totalQuarter1txt.text forKey:#"totalOfTheQuater"];
[quarter setObject:showQuartertxt.text forKey:#"quarterP"];
[quarter setObject:[NSString stringWithFormat:#"%d",(int)slider4.value]forKey:#"quartersliderValue"];
}
QuarterValueArray =[[[NSUserDefaults standardUserDefaults]arrayForKey:#"QuarterValues"]mutableCopy];
Why this code is crashing when the method is called second time? The crash log says:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'
Try this.
NSMutableArray *quarterValueArray = [[[NSUserDefaults standardUserDefaults]arrayForKey:#"QuarterValues"] mutableCopy];
if (!quarterValueArray)
{
NSMutableArray *aQuarterArray = [[NSMutableArray alloc] init];
for (int i = 0; i < 12; i++)
{
NSMutableDictionary *dict1 = [[[NSMutableDictionary alloc] init] mutableCopy];
[dict1 setObject:#"" forKey:#"quarterP"];
[dict1 setObject:#"" forKey:#"quartersliderValue"];
[dict1 setObject:#"" forKey:#"totalOfTheQuater"];
[aQuarterArray addObject:dict1];
}
[[NSUserDefaults standardUserDefaults] setValue:aQuarterArray forKey:#"QuarterValues"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
[self saveUserDefaultDataQuarter];
NSLog(#"%#",quarterValueArray);
and modify like this.
-(void)saveUserDefaultDataQuarter{
NSMutableArray*QuarterValueArray =[[[NSUserDefaults standardUserDefaults]arrayForKey:#"QuarterValues"]mutableCopy];
NSMutableDictionary *quarter = [QuarterValueArray objectAtIndex:0];
[quarter setValue:totalQuarter1txt.text forKey:#"totalOfTheQuater"];
[quarter setValue:showQuartertxt.text forKey:#"quarterP"];
[quarter setValue:[NSString stringWithFormat:#"%d",(int)slider4.value]forKey:#"quartersliderValue"]; }
UPDATE :I have used set value for setting the dictionary key values, as dictionary is already created when we are modifying values.

NSMutableArray isn't adding my NSString

Im trying to add a NSString to an NSMutableArray and then make the array into NSData and save it with NSUserDefaults. But the array is always nil.
Here is my code:
- (void)viewDidLoad {
[super viewDidLoad];
library = [[ALAssetsLibrary alloc] init];
groups = [[NSMutableArray alloc] init];
userDefaults = [[NSUserDefaults alloc] init];
NSData *data = [userDefaults objectForKey:#"GroupArray"];
groups = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"%i", [groups count]);
}
-(IBAction)newFolder {
if (textField.text != nil) {
NSString *string = textField.text.capitalizedString;
[library addAssetsGroupAlbumWithName:string
resultBlock:^(ALAssetsGroup *group) {
NSLog(#"Created a folder named: %#", group);
}
failureBlock:^(NSError *error) {
NSLog(#"An error occured! - %#", error);
}
];
[groups addObject:string];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:groups];
[userDefaults setObject:data forKey:#"GroupArray"];
NSLog(#"%i", [groups count]);
}
[self.view sendSubviewToBack:subView];
}
When the app starts i get a message in the console that the array is NULL. When I try to add the string the NSLog(#"%i", [groups count]); always return 0.
Why does this happen?
userDefaults = [[NSUserDefaults alloc] init];
NSData *data = [userDefaults objectForKey:#"GroupArray"];
In this case, data will be nil when the code is executed for the first time, since there is yet no "GroupArray" property present.
groups = [NSKeyedUnarchiver unarchiveObjectWithData:data];
This causes groups be become nil as well, because calling unarchiveObjectWithData: with nil as an argument will return nil as well.
And because of all that, in -newFolder, [groups addObject:string] becomes [nil addObject:string]
Calling a method on nil is allowed in Objective-C, so you get no exception there. The return value of any method called on nil is, again, nil or 0.
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:groups];
This causes data to be nil because groups is nil.
So you are always calling:
[userDefaults setObject:data forKey:#"GroupArray"];
with data = nil
Quick fix:
Add if (groups == nil) groups = [NSMutableArray array]; to the beginning of -newFolder
In view didLoad method every time you are initialising new instance of NSUserDefaults and calling
NSData *data = [userDefaults objectForKey:#"GroupArray"]; on the newly created object
which will return nil all the time because in the new instance there wont be any object for key #"GroupArray". Instead replace userDefaults with singleton object [NSUserDefaults standardUserDefaults]
Modify your code as shown below
- (void)viewDidLoad
{
[super viewDidLoad];
library = [[ALAssetsLibrary alloc] init];
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:#"GroupArray"];
groups = [NSKeyedUnarchiver unarchiveObjectWithData:data];
if (!groups)
{
groups = [[NSMutableArray alloc] init];
}
NSLog(#"group %#", groups);
NSLog(#"%i", [groups count]);
}
Your newFolder method
-(IBAction)newFolder
{
if (textField.text != nil)
{
NSString *string = textField.text.capitalizedString;
[library addAssetsGroupAlbumWithName:string
resultBlock:^(ALAssetsGroup *group) {
NSLog(#"Created a folder named: %#", group);
}
failureBlock:^(NSError *error) {
NSLog(#"An error occured! - %#", error);
}
];
[groups addObject:string];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:groups];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"GroupArray"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"%i", [groups count]);
}
[self.view sendSubviewToBack:subView];
}
This should work. As it worked for me.

How to save data locally in app? [duplicate]

This question already has answers here:
Working with data in iOS Apps (What to choose? NSData, CoreData, sqlite, PList, NSUserDefaults)
(2 answers)
Closed 9 years ago.
I've been struggling with this for ages now and I really need some good help here. :)
I have an app where I'm parsing a quite big JSON into appdelegate's didFinishLaunchingWithOptions.
My Model Objects are:
Tab:
NSString *title
NSMutableArray *categories
Category:
NSString *title
NSMutableArray *items
Item
NSString *title
NSString *description
UIImage *image
I need to save the data locally, cause the parsing takes about 15 seconds every time my app starts. I'm using the SBJSON framework.
Here's my code for parsing:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"json_template" ofType:#"json"];
NSString *contents = [NSString stringWithContentsOfFile: filePath encoding: NSUTF8StringEncoding error: nil];
SBJsonParser *jsonParser = [[SBJsonParser alloc] init];
NSMutableDictionary *json = [jsonParser objectWithString: contents];
tabs = [[NSMutableArray alloc] init];
jsonParser = nil;
for (NSString *tab in json)
{
Tab *tabObj = [[Tab alloc] init];
tabObj.title = tab;
NSDictionary *categoryDict = [[json valueForKey: tabObj.title] objectAtIndex: 0];
for (NSString *key in categoryDict)
{
Category *catObj = [[Category alloc] init];
catObj.name = key;
NSArray *items = [categoryDict objectForKey:key];
for (NSDictionary *dict in items)
{
Item *item = [[Item alloc] init];
item.title = [dict objectForKey: #"title"];
item.desc = [dict objectForKey: #"description"];
item.url = [dict objectForKey: #"url"];
if([dict objectForKey: #"image"] != [NSNull null])
{
NSURL *imgUrl = [NSURL URLWithString: [dict objectForKey: #"image"]];
NSData *imageData = [NSData dataWithContentsOfURL: imgUrl];
item.image = [UIImage imageWithData: imageData];
}
else
{
UIImage *image = [UIImage imageNamed: #"standard.png"];
item.image = image;
}
[catObj.items addObject: item];
}
[tabObj.categories addObject: catObj];
}
[tabs addObject: tabObj];
}
What is the best way of doing this? Using Core Data or NSFileManager?
If you have som code example too it will make me very happy.
This is the last thing i need to fix before the app is ready for app store and it just kills me! I can't solve this problem.
If you are working on iOS then you save a file to the Documents folder. On Mac OS X it would be in the Application Support folder. Since you are on iOS, read this answer for how to access the Documents folder.
All of the objects that you want to store should implement NSCoding. The above variables already do. Should you want to store the tabs, categories and items directly they would need to implement NSCoding. Then all you need is to serialize them to a file. When opening you app you can look for this file and get your objects back without parsing.
The code should look something like this (untested and error checking is ommited for brevity):
- (void) saveStateToDocumentNamed:(NSString*)docName
{
NSError *error;
NSFileManager *fileMan = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [paths[0] stringByAppendingPathComponent:docName];
if ([fileMan fileExistsAtPath:docPath])
[fileMan removeItemAtPath:docPath error:&error];
// Create the dictionary with all the stuff you want to store locally
NSDictionary *state = #{ ... };
// There are many ways to write the state to a file. This is the simplest
// but lacks error checking and recovery options.
[NSKeyedArchiver archiveRootObject:state toFile:docPath];
}
- (NSDictionary*) stateFromDocumentNamed:(NSString*)docName
{
NSError *error;
NSFileManager *fileMan = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [paths[0] stringByAppendingPathComponent:docName];
if ([fileMan fileExistsAtPath:docPath])
return [NSKeyedUnarchiver unarchiveObjectWithFile:docPath];
return nil;
}

Removing objects from array in NSUserDefaults

When setting NSUserDefaults, I was initially using this code to set the defaults...
NSMutableArray *array = [NSMutableArray arrayWithObjects: #"string1", #"string2", #"string3", nil];
[[NSUserDefaults standardUserDefaults] setObject:array forKey: #"preset1"];
[[NSUserDefaults standardUserDefaults] synchronize];
I've learned that I should be using this instead:
NSMutableArray *array = [NSMutableArray arrayWithObjects: #"string1", #"string2", #"string3", nil];
NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:array forKey:#"preset1"];
[[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
Now I'm having an issue manipulating the objects later on in array. Here is the code I use to add/remove strings from array. It worked fine when I was initially setting the defaults manually in my first example. Now, the objects will not remove from the array. I did notice when printing the array in LLDB debugger that array is now being stored as a NSCFArray when it was just an NSArray before.
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObjectsFromArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"preset1"]];
NSArray *stringsToRemove = #[#"string1", #"string2" ];
for (NSUInteger i = 0; i < stringsToRemove.count; i++) {
[array removeObjectIdenticalTo:[stringsToRemove objectAtIndex:i]];
}
[[NSUserDefaults standardUserDefaults] setObject:array forKey: #"preset1"];
[[NSUserDefaults standardUserDefaults] synchronize];
This code works for me with your setup, after initializing the defaults the second way you described:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *array = [[NSMutableArray alloc] initWithArray: [defaults objectForKey:#"preset1"]];
NSArray *stringsToRemove = [NSArray arrayWithObjects:#"string1", #"string2", nil];
for (NSString *aString in stringsToRemove) {
[array removeObjectIdenticalTo:aString];
}
[defaults setObject:array forKey: #"preset1"];
[defaults synchronize];