userDefaults and comparing arrays - ios

I got problem with saving and comparing data. First I download data and save my data (array,dict)to userDefaults. After redownloading I need to compare if my new array have some new data which I haven't saved in userDefaults. So its mean I should find the data that is not the same inside my old data and add them to my new array.
NSMutableDictionary* tmpDict = [NSMutableDictionary new];
NSMutableDictionary* copyDict = [NSMutableDictionary new];
NSMutableArray *dataGroupsArr = [NSMutableArray new];
NSMutableDictionary *dataGroupsDict = [NSMutableDictionary new];
dataGroupsDict[#"name"] = #"FirstGroup"; // I dont need groups at the moment
NSMutableArray *datas = [[NSMutableArray alloc]init];
FOR ........ (parser)
{
NSMutableDictionary *data = [[NSMutableDictionary alloc]init];
data[#"dataID"] = [#"some data from parser"];
[datas addObject:data];
}];
dataGroupsDict[#"datas"] = datas;
[dataGroupsArr addObject:dataGroupsDict];
tmpDict[#"dataGroups"] = dataGroupsArr;
After I save data Im trying to load them from userdefualts
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
copyDict = [userDefaults objectForKey:#"dataDownloadArr"];
//data never added
if(copyDict == nil){
[userDefaults setObject:tmpDict forKey:#"dataDownloadArr"];
[userDefaults synchronize];
} else {
for (int i = 0; i< [copyDict[#"dataGroups"][0][#"datas"]count]; i++){
NSLog(#"%#", copyDict[#"dataGroups"][0][#"datas"][i][#"dataID"]);
}
}
Now I don't know how to compare data, and if there is new data in my new array how to add it to old one.

Did you try to use NSData instance method isEqualToData?
Two data objects are equal if they hold the same number of bytes, and
if the bytes at the same position in the objects are the same.
https://developer.apple.com/documentation/foundation/nsdata/1409330-isequaltodata

I was also facing same issue what I did is I asked the backend developer to send a value as modified_date and create a dictionary containing dataArray and modified_date. So after re-downloading you can just check for modified_date and replace the dataArray instead of comparing every array element.

Related

NSUserDefaults save two arrays leading to crash

Recently I was studying NSUserDefaults, then made a demo as follows:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *activity_array = [NSMutableArray array];
NSMutableArray *movie_array = [NSMutableArray array];
[defaults setObject:activity_array forKey:#"activity"];
[defaults setObject:movie_array forKey:#"movie"];
[defaults synchronize];
Then I tried writing the following which I will be calling "code2" for the duration of this post:
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
NSMutableArray *array = [userDefault objectForKey:#"activity"];
[array addObject:#"123"];
the demo still works.
However the demo crashes when I replace "code2" with the following code:
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
NSMutableArray *array = [userDefault objectForKey:#"movie"];
[array addObject:#"123"];
As you can see, the difference is the key.
Why does this crash?
NSUserDefaults can store NSMutableArrays, but will turn them into immutable arrays.
Same goes for NSDictionary and NSMutableDictionary.
That means if you want to add an object to an array that you just extracted from the NSUserDefaults, you will have to first make it mutable first, using -mutableCopy for example, or -initWithArray.
NSArray *array = [userDefault objectForKey:#"movie"];
//This works
NSMutableArray *arr = [NSMutableArray alloc]initWithArray:array];
//This works too and is more commonly used.
NSMutableArray *arr = [array mutableCopy];
You can now modify the array arr without any trouble, and you will be able to save it just like you did before. If you retrieve it afterwards, it will be the modified array. But be careful, arrays and dictionaries are always immutable when taken from NSUserDefaults. You will have to do that "trick" everytime you want to modify an array or dictionary from the NSUserDefaults.
EDIT : after testing your code, my only assumption is that your crash-free array is simply nil when you retrieve it. Debug with breakpoints to verify this but I'm close to 101% sure.
EDIT2 : trojanfoe got that faster than I did !
As others have pointed-out the arrays you get back from NSUserDefaults are immutable, so an exception will be thrown when calling addObject: on them, however that won't occur if the array is nil as calling methods (sending messages) to objects that are nil are silently ignored.
Therefore I believe your code2 works as the object #"activity" doesn't exist in the user defaults, while #"movie" does.
Arrays and dictionaries returned from NSUserDefaults are always immutable, even if the one you set was mutable. You'll have to call -mutableCopy.
Try this:
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
NSMutableArray *array = [[userDefault objectForKey:#"movie"]mutableCopy];
The object from userDefault is not mutable.. Try this
NSMutableArray *arr = (NSMutableArray *)[userDefault objectForKey:#"activity"];
or if you like use id and check its class first to prevent crashing:
id variableName = [userDefault objectForKey:#"activity"];
if ([[variableName class] isEqual:[NSArray class]])
{
NSMutableArray *arr = [[NSMutableArray alloc] initWithArray:(NSArray *)variableName];
NSMutableArray *arr = [(NSArray *)variableName mutableCopy];
}
else if ([[variableName class] isEqual:[NSNull class]])
NSLog(#"no object with key:activity");
else
NSLog(#"not array");
//Happy coding.. :)

how to save an NSMutableArray to local storage

I want to save an NSMutableArray to a file and load it every time my program runs. What is the best solution for this?
This is my example:
// I add a new item to ARRAYLIST
NSString *textNewCategory = [alert textFieldAtIndex:0].text;
[ARRAYLIST insertObject:textNewCategory atIndex:0];
// I save ARRAYLIST
userCategory = [NSUserDefaults standardUserDefaults];
[userCategory setObject:ARRAYLIST forKey:#"newCategory"];
NSLog(#"ARRAYLIST 1:%d", ARRAYLIST.count); // EX: In this case, it has count : 30
//In viewdidload, I get array that I saved before
userCategory = [NSUserDefaults standardUserDefaults];
arrayNewCategory = [[NSMutableArray alloc] initWithArray: [userCategory objectForKey:#"newCategory"]];
ARRAYLIST = arrayNewCategory;
NSLog(#"ARRAYLIST 2:%d", ARRAYLIST.count); // In this moment, it only has count: 29.
I don't understand why the re-loaded array has only 29 items instead of the 30 that I expect.
Save the array to a plist:
[array writeToFile:path atomically:NO]
and retrieve it:
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:path];

Edit an object in an NSMutableArray saved with NSUserDefaults

i'm getting [__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object when I try to edit an object which I get from NSUserDefault.
I have searched through stackoverflow and tried making the mutable copy after retrieving the data but still getting the error. Here is the current code
I set this in the first view controller
NSMutableArray *settings=[[NSMutableArray alloc]init];
[settings addObject:[#{
#"single":#"Player1",
#"multiplesame":[[NSMutableArray alloc] initWithObjects:#"Player1",#"Player2",#"Player3",#"Player4",#"Player5", nil],
#"multipleDiff":[UIDevice currentDevice].name
}mutableCopy]];
[Reusables storeArrayToDefaults:SETTINGS_DETAILS objectToAdd:settings];
Now in another view controller, I'm trying to change the value of Player1 of single key to a name. For that this is how I retrieve the data.
NSMutableArray *setDetails = [[[NSUserDefaults standardUserDefaults] arrayForKey:SETTINGS_DETAILS] mutableCopy];
//setDetails= [[NSMutableArray alloc] initWithArray:[[NSUserDefaults standardUserDefaults] arrayForKey:SETTINGS_DETAILS]] ;
//setDetails = [setDetails mutableCopy];
NSLog(#"Details:%#",setDetails);
The log prints fine
Details:(
{
multipleDiff = "iPhone Simulator";
multiplesame = (
Player1,
Player2,
Player3,
Player4,
Player5
);
single = Player1;
}
)
But now when I try to change the value like this it gets crashed with the above error
[[setDetails objectAtIndex:0] setObject:#"Any Name" forKey:#"single"] ;
For reference this is how I save in NSUserDefaults
+(void)storeArrayToDefaults :(NSString *)keyName objectToAdd:(NSMutableArray *)arrayData
{
NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
[defaults setObject:arrayData forKey:keyName];
[defaults synchronize];
}
Try this
NSMutableArray *setDetails = [NSMutableArray arrayWithArray:[[[NSUserDefaults standardUserDefaults] arrayForKey:SETTINGS_DETAILS] mutableCopy]];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:[setDetails objectAtIndex:0]];
[dict setObject:#"Any Name" forKey:#"single"];
[setDetails replaceObjectAtIndex:0 withObject:dict];
Everything in NSUserDefaults is immutable. You create a mutable copy of the outer container but its contents are still immutable. So, to edit them, you need to take a mutable copy, update that and then reinsert (and re-add to defaults).
[[setDetails objectAtIndex:0] setObject:#"Any Name" forKey:#"single"];
changes to
NSMutableDictionary *dict = [[setDetails objectAtIndex:0] mutableCopy];
[dict setObject:#"Any Name" forKey:#"single"];
[setDetails replaceObjectAtIndex:0 withObject:dict];

NSArray is not saving in NSUserDefaults [duplicate]

This question already has answers here:
How to store custom objects in NSUserDefaults
(7 answers)
Closed 9 years ago.
I am making a high score manager.
I load and save as follows:
-(void)load
{
NSMutableArray* scores = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:HIGH_SCORE_KEY]];
if(scores == nil)
{
highscores = [NSMutableArray array];
}
else
{
highscores = scores;
}
}
-(void)save
{
[[NSUserDefaults standardUserDefaults] setObject:highscores forKey:HIGH_SCORE_KEY];
}
I am storing HighScore objects:
#import <Foundation/Foundation.h>
#interface HighScore : NSObject
{
int score;
NSString* name;
}
-(int)score;
-(NSString*)name;
-(id)init;
-(id)initWithName:(NSString*)playerName andScore:(int)playerScore;
-(BOOL)isEqual:(id)object;
-(BOOL)isLessThan:(id)object;
#end
There is nothing particularly complex about this class. However, when I load, the load does not return nil but returns an empty array, indicating to me that serializing probably failed for some reason.
Any ideas?
Thanks
NSUserDefaults always returns immutable objects, even if the original object was mutable. It's in the documentation for objectForKey:
The returned object is immutable, even if the value you originally set was mutable.
You will need to create a copy of the returned object before you modify it, using [NSMutableArray arrayWithArray:]
Probably also best to use the arrayForKey method of NSUserDefaults if you're retrieving an array. Docs here: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html
You can only store objects in NSUserDefaults. So you have to convert the array to an NSArray. However, NSArrays also only store objects, so you need to store the long values encapsulated in an NSNumber object:
//saving
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *arrayObj = [[NSMutableArray alloc] init];
long *arr;
arr = new long [10];
for(int i = 0 ; i<10 ; i++) {
[arrayObj addObject:[NSNumber numberWithLong:arr[i]]];
}
[standardDefaults setObject:arrayObj forKey:#"longArray"];
[arrayObj release];
//reading
NSUserDefaults *standardDefaults = [NSUserDefaults standardUserDefaults];
NSArray *arrayObj = [standardDefaults objectForKey:#"longArray"];
long *arr;
arr = new long [10];
for(int i = 0 ; i<10 ; i++) {
arr[i] = [(NSNumber*)[arrayObj objectAtIndex:i] longValue];
}

NSMutableDictionary -- using allKeysforObject not retrieving array values

NSMutableDictionary *expense_ArrContents = [[NSMutableDictionary alloc]init];
for (int i = 1; i<=4; i++) {
NSMutableArray *current_row = [NSMutableArray arrayWithObjects:#"payer_id",#"Expense_Type_id",#"Category_Id",#"SubCategory_Id",nil];
[expense_ArrContents setObject:current_row forKey: [NSNumber numberWithInt:i]];
}
NSArray *newArray = [expense_ArrContents allKeysForObject:#"payer_id"];
NSLog(#"%#",[newArray description]);
i want to get the list of key values containing the particular object which is in the array of values stored in nsmutabledictionary for a particular key.
In the line where you get all the keys ([expense_ArrContents allKeysForObject:#"payer_id"];) you actually get keys for an object that is not in any of the array's items. This #"player_id" is different object than the #"player_id" you added in current_row. In fact, maybe all of your rows have different #"player_id" objects (except if the compiler has made some optimization - maybe it threats that same string literal as one object instead of creating new object for each iteration).
Try creating an NSString object for the #"player_id" which you add to the current_row and then get all the keys for that same object:
NSString* playerId = #"player_id";
for(){
NSMutableArray *current_row = [NSMutableArray arrayWithObjects: playerId,...];
...
}
NSArray *newArray = [expense_ArrContents allKeysForObject:playerId];
Your NSArray *newArray = [expense_ArrContents allKeysForObject:#"payer_id"]; will not return any value because in expense_ArrContents there is no such key(#"payer_id"), instead there are keys like 1,2,3 etc.What is your requirement?Want to see what all keys are there in expense_ArrContents just log
NSArray*keys=[expense_ArrContents allKeys];
Try this :
NSMutableArray *array_key=[[NSMutableArray alloc]init];
for (NSString *key in expense_ArrContents) {
if ([[expense_ArrContents objectForKey:key] containsObject:#"payer_id"]) {
[array_key addObject:key];
}
}

Resources