NSArray is not saving in NSUserDefaults [duplicate] - ios

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];
}

Related

userDefaults and comparing arrays

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.

Problems with NSMutableDictionary and NSMutableArray ( Mutating Error - but why? ) [duplicate]

The following code is returning an exception with the following error message "mutating method sent to immutable object" when attempting to removeObjectForKey
NSMutableDictionary * storedIpDictionary = (NSMutableDictionary*)[[NSUserDefaults standardUserDefaults] dictionaryForKey:#"dictDeviceIp"];
NSString *key = self.currentDeviceNameText.text;
NSString *ipAddressTemp = [storedIpDictionary objectForKey:key];
[storedIpDictionary removeObjectForKey:key]; <----Crashes here
storedIpDictionary[key] = ipAddressTemp;
Not sure what the issue is, perhaps it is due to retrieving the dictionary from a NSUserDefaults.
However the following code works without any issues.
NSMutableDictionary * storedIpDictionary = (NSMutableDictionary*)[[NSUserDefaults standardUserDefaults] dictionaryForKey:#"dictDeviceIp"];
[storedIpDictionary removeAllObjects];
NSUserDefaults returns immutable objects, even if you put in mutable ones. You must call -mutableCopy on the returned value to get a mutable collection.
You cant just cast an NSDictionary to NSMutableDictinary thats not at all how casting works.
to remove a key from NSUserDefualts call removeObjectForKey on the NSUserDefaults instance itself.
if you really do want a dictionary for some other reason, then you must make a mutableCopy from the dictionary obtained by dictionaryForKey.
This is the code that eventually worked, I used some of the details provided from others above, but none had it completely explained.
- (void)cleanDictionary
{
NSMutableDictionary * storedIpDictionary = [[[NSUserDefaults standardUserDefaults] objectForKey: #"dictDeviceIp"] mutableCopy];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"dictDeviceIp"];
NSString *oldKey = self.currentDeviceNameText.text;
NSString *newKey = self.deviceNameChangeText.text;
NSString *ipAddressTemp = [storedIpDictionary objectForKey:oldKey];
// Make some change to the structure
[storedIpDictionary removeObjectForKey:oldKey]; // Remove object
storedIpDictionary[newKey] = ipAddressTemp; // Add object with new key
// Add it the whole thing back into NSUserDefaults
[[NSUserDefaults standardUserDefaults] setObject:storedIpDictionary forKey:#"dictDeviceIp"];
// Synchronize to ensure it's saved
[[NSUserDefaults standardUserDefaults] synchronize];
}
if you have on error NSMutableDictionary: mutating method sent to immutable object in Swift, make this step:
This is because you have assigned a NSUserDefault to NSMutableArray, when you take something NSUserDefault it returns you a NSArray not a NSMutableArray, so in this case you have to use a NSMutableArray Auxiliary .
see for Swift :
var Products:NSMutableArray = NSMutableArray()
override func viewDidAppear(animated: Bool) {
if let Produtos = NSUserDefaults.standardUserDefaults().valueForKey("Produtos") {
Products = Produtos as! NSMutableArray
}
}
func InsertProducts(productCode:String){
//COPY Products Atual for auxMutable
var auxMutable = Products.mutableCopy()
//Add object in auxMutable
auxMutable.addObjectsFromArray([productCode])
//in line back data to Array Products and make cast to NSMutableArray
Products = auxMutable as! NSMutableArray
//Refresh Data of NSUserDefaults
NSUserDefaults.standardUserDefaults().setObject(Products, forKey: "Produtos")
}
#IBAction func Bt_New_Product(sender: AnyObject) {
var ProductName:String = TXT_NameProduct.text
InsertProducts(ProductName)
}
This work for me!!!
i found same issue and found solution hope it will help some one.
arrayOfferId = defaults.objectForKey("offerId")?.mutableCopy() as! NSMutableArray
NSUserDefaults returns immutable objects, even if you put in mutable ones. You must call -mutableCopy on the returned value to get a mutable collection. so when you get value from NSUserDefault use mutableCopy()
[NSUserDefaults dictionaryForKey] returns an immutable dictionary (NSDictionary) and you cannot force it to be mutable by casting it to NSMutableDictionary.
Instead you must create the mutable dictionary using mutableCopy, overwrite the element and then re-assigning the dictionary back into NSUserDefaults:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *storedIpDictionary = [[userDefaults dictionaryForKey:#"dictDeviceIp"] mutableCopy];
NSString *key = self.currentDeviceNameText.text;
NSString *ipAddressTemp = [storedIpDictionary objectForKey:key];
// Don't need this line
//[storedIpDictionary removeObjectForKey:key];
storedIpDictionary[key] = ipAddressTemp;
[userDefaults setObject:storedIpDictionary
forKey:#"dictDeviceIp"];

Alternative of ArrayList in iOS?

I have parsed a JSON that resulted in array of elements. I wish to store it in a an array just like we would add elements dynamically to arraylist and access it later. What is the alternative of ArrayList in case of iOS for adding elements dynamically and retrieving it later. Is there any other unique options available in iOS for storing and retrieving elements dynamically?
Data1:
Reference number: 1241
InvoiceNumber: 1421241
InvoiceDate: 06-FEB-16
Data2:
Reference number: 3241
InvoiceNumber: 12421
InvoiceDate: 06-FEB-16
Above are the two data with unique elements that I got by parsing a json.
In class A , Im adding the following code to store the nsmutablearray in nsuserdefaults,
for (NSUInteger i = 0; i < savecount; i++) {
NSMutableArray *newArray = [[NSMutableArray alloc] init];
[myArrays addObject:newArray];
[newArray addObject:shipmentReferenceNumber];
[[NSUserDefaults standardUserDefaults] setObject:newArray forKey:#"shipmentReferenceNumber "];
}
In class B, Im trying to access it by using the following code but it returns null
NSString *referenceNumber = [[NSUserDefaults standardUserDefaults] stringForKey:#"referenceNumber"];
In objC and swift, An ArrayList is a NSMutableArray
A hash map like functionality (store key value pairs. keys are unique) is offered by NSDictionary
To used Hash Map with NSArray. Used following code.
In Class A while Storing:
NSMutableArray* arrayList = [[NSMutableArray alloc] init];
for (int i=0; i<savecount; i++) {
NSDictionary* innerDict = [NSDictionary dictionaryWithObjectsAndKeys:your_referenceNo,#"referenceNo",your_InvoiceNumber,#"invoiceNumber",your_InvoiceDate,#"invoiceDate", nil];
[arrayList addObject:innerDict];
}
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:arrayList];
[[NSUserDefaults standardUserDefaults] setObject:encodedObject forKey:#"data"];
In Class B while retraving:
NSData* data = [[NSUserDefaults standardUserDefaults] objectForKey:#"data"];
NSMutableArray* arrayList = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"Reference No : %#",arrayList[0][#"referenceNo"]);
Edit: to dynamic access into arraylist
for(int i=0; i<[arrayList count];i++){
NSLog(#"Reference No : %#",arrayList[i][#"referenceNo"]);
}
Thanks
NSMutableArray can be used if the array is editable, else an NSArray.
Forgive me if I misunderstand what you are trying to achieve, but I would consider creating a Data model object:
#interface Data : NSObject
#property (nonatomic) NSInteger referenceNumber;
#property (nonatomic) NSInteger invoiceNumber;
#property (strong, nonatomic) NSString *invoiceDate;
#end
You could then create an array of data objects:
#property (strong, nonatomic) NSMutableArray <Data *> *dataArray;
Here a data struct, which dynamically increased and gets by index, basically, it is a normal linked list class, with a dynamic array inside to provide getting data by index, it duplicates the space, but you may delete the linked list after updating the array, see the code:
struct LinkedNode
{
long data;
LinkedNode* next;
};
class LinkedList
{
public:
LinkedList();
~LinkedList();
LinkedNode* head;
long Count;
long * Data;
void add(long data);
void update();
//long get(long index);
};
LinkedList::LinkedList(){
this->Count = 0;
this->head = NULL;
}
LinkedList::~LinkedList(){
LinkedNode * temp;
while(head){
temp= this->head ;
head = head->next;
delete temp;
}
if (Data)
delete [] Data; Data=NULL;
}
void LinkedList::add (long data){
LinkedNode * node = new LinkedNode();
node->data = data;
node->next = this->head;
this->head = node;
this->Count++;}
void LinkedList::update(){
this->Data= new long[this->Count];
long i = 0;
LinkedNode * node =this->head;
while(node){
this->Data[i]=node->data;
node = node->next;
i++;
}
}
If you use this, please refer to my work https://www.liebertpub.com/doi/10.1089/big.2018.0064

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.. :)

Add NSArray To NSMutableArray

Thanks for you time and reading this. What I'm trying to do is figure out why this NSLog is telling me the NSArray is always null, no matter what. I'm thinking that the problem is that I'm initiating the NSMutableArray wrong. Could you perhaps take a look and decide whether or not I did it right, and if at all possible give me a way to pass the array into the NSMutableArray?
Thanks!
//Get Defaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *favoriteArray = [[defaults objectForKey:#"favorites"] copy];
//Declares Mutable Array
self.favorites = [[NSMutableArray alloc] initWithObjects:favoriteArray, nil];
NSLog(#"array: %#", favorites);
UPDATE: I figured it out. It turns out you have to declare it with initWithArray rather than trying to add it as an object
Solution:
- (void)viewDidLoad {
//Get Defaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *favoriteArray = [[defaults objectForKey:#"favorites"] copy];
//Declares Mutable Array
self.favorites = [[NSMutableArray alloc] initWithArray:favoriteArray];
[super viewDidLoad];
}
The way to do this is using the arrayWithArray and here is how you do it:
myNSMutableArray = [NSMutableArray arrayWithArray:myArray];
Do you ever set an object in your user defaults for the "favorites" key?

Resources