Save NSArray to NSUserDefaults issues (using custom object in array) - ios

I try to save my object to NSUserDefaults. But when I call this method again it is not have any info about previous operation.
There is my method below:
- (void)addToCart {
if([[NSUserDefaults standardUserDefaults] objectForKey:kCart]) {
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSMutableArray *products = [[NSMutableArray alloc] initWithArray:[prefs objectForKey:kCart]];
[products addObject:self.product];
[prefs setObject:products forKey:kCart];
[prefs synchronize];
[products release];
}
else {
//Saving...
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setObject:[NSArray arrayWithObjects:self.product, nil] forKey:kCart];
[prefs synchronize];
}
}
I need to save a collection with a products to NSUserDefault. I wrap my object to NSArray and save it but it doesn't work.

Everything put into NSUserDefaults must be a valid property list object (NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary). All collection elements must themselves also be property list objects.
In order to save non-PL objects into NSUserDefaults, you must first convert the object into a PL object. The most generic way to do this is by serializing it to NSData.
Serializing to NSData is handled with NSKeyedArchiver. See Storing NSColor in User Defaults for the canonical example of this. (That document is very old and still references NSArchiver which will work fine for this problem, but NSKeyedArchiver is now the preferred serializer.)
In order to archive using NSKeyedArchiver, your object must conform to NSCoding as noted by #harakiri.

You need to conform to the <NSCoding> protocol and implement -initWithCoder: and -encodeWithCoder: in your custom object.
See: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSCoding_Protocol/Reference/Reference.html

Related

Clear data of a particular NSUserDefaults object Data

In my project I am using NSUseDefaults for store data with the different objects.
NSUserDefaults *defaults1=[NSUserDefaults standardUserDefaults];
//---- I have set object for this
[defaults1 synchronize];
NSUserDefaults *defaults2=[NSUserDefaults standardUserDefaults];
//---- I have set object for this
[defaults2 synchronize];
Now I want clear all keys data only for defaults2, not for defaults1. So whenever I am applying below code:
NSDictionary *defaultsDictionary = [defaults2 persistentDomainForName: appDomain];
for (NSString *key in [defaultsDictionary allKeys]) {
NSLog(#"removing user pref for %#", key);
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
}
Above code have remove value for defaults2 but also for defaults1. But I don't want to remove objects for defaults1. So please help me out.
NSUserDefaults is like a singelton class so it will always return the same shared system object.
You can store multiple objects using multiple keys and can delete/remove objects against those keys.
If you have read a doc about NSUserDefaults standardUserDefaults you should know that standardUserDefaults Returns the shared defaults object. and actually defaults1 and defaults2 the same.
You can store keys and then delete only those keys like:
NSUserDefaults *defaults1=[NSUserDefaults standardUserDefaults];
//---- I have set object for this
[defaults1 synchronize];
[[defaults1 dictionaryRepresentation] allKeys]; // use this keys for deleting

Save id type object in NSUserDefaults

I want to save id type object value in NSUserDefaults.
-(IBAction)sendOrderReady:(id)sender
{
NSUserDefaults *d = [NSUserDefaults standardUserDefaults];
[d setValue:sender forKey:#"sender"];
// [d setObject:sender forKey:#"sender"];
[d synchronize];
}
from the NSUserDefaults class description
The NSUserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Booleans, and URLs. A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData.
so if your id object isn't one of the above instances, then you will have to convert it to one of them, and this question is what exactly you're looking for.
With NSUserDefaults you can save objects from the following class types:
NSData, NSString, NSNumber, NSDate, NSArray, NSDictionary
If you want to store any other type of object then you need to archive it or wrap it in an instance of NSData
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// saving an NSString
[prefs setObject:#"iPhone" forKey:#"keyToLookupString"];
// saving an NSInteger
[prefs setInteger:442 forKey:#"integerKey"];
// saving a Double
[prefs setDouble:5.15 forKey:#"doubleKey"];
// saving a Float
[prefs setFloat:123.45678 forKey:#"floatKey"];
// This is suggested to synch prefs, but it is not needed
[prefs synchronize];

iOS NSDictionary saved to NSUserDefaults not saving values

I am trying to save a NSDictionary with array values to NSUserDefaults but am having some strange trouble.
My NSDictionary has NSStrings for keys and each value is a NSArray of NSNumbers. When I print the dictionary out, everything is fine. I write this dictionary to NSUserDefaults and if I read it back out right away, everything seams fine. Using this everything seams just fine:
[[NSUserDefaults standardUserDefaults] setObject:self.selectedOptionPositions
forKey:PREF_OPTIONS_KEY];
[[NSUserDefaults standardUserDefaults] synchronize];
//THIS PRINT EVERYTHING OUT EXACTLY AS IT SHOULD!
NSLog(#"read after write: %#", [[NSUserDefaults standardUserDefaults]
objectForKey:PREF_OPTIONS_KEY]);
The problem comes when I create a new instance of the class that handles this. When I make a new instance of the class and in the init method check the NSDictionary like so:
NSLog(#"read initial: %#", [[NSUserDefaults standardUserDefaults]
objectForKey:PREF_OPTIONS_KEY]);
When I print that logging, the NSDictionary contains all of the keys but all of the values are now empty! All newly added keys exist after recreating the class, but no values persist.
What could be wrong here? There are no warnings or errors in the console.
Try this:
You can use NSKeyedArchiver to write out your dictionary to an NSData, which you can store among the preferences.
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.selectedOptionPositions];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:PREF_OPTIONS_KEY];
For retrieving data:
NSData *dictionaryData = [defaults objectForKey:PREF_OPTIONS_KEY];
NSDictionary *dictionary = [NSKeyedUnarchiver unarchiveObjectWithData:dictionaryData];
As in the iOS Developer Documentation for NSKeyedArchiver it says that:
NSKeyedArchiver, a concrete subclass of NSCoder, provides a way to
encode objects (and scalar values) into an architecture-independent
format that can be stored in a file. When you archive a set of
objects, the class information and instance variables for each object
are written to the archive. NSKeyedArchiver’s companion class,
NSKeyedUnarchiver, decodes the data in an archive and creates a set of
objects equivalent to the original set.

How to save custom objects inside of an array that holds more custom objects

I have an array that is broken down like this:
PersonArray
PersonObject
1. NSstring (personsName)
2. NSMutableArray (EventsObject)
I am able to save this array with the code below:
NSMutableArray *archiveArray = [NSMutableArray arrayWithCapacity:mutableDataArray.count];
for (BC_Person *personObject in mutableDataArray)
{
NSData *personEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:personObject];
[archiveArray addObject:personEncodedObject];
}
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:archiveArray forKey:#"personDataArray"];
[userData synchronize];
What I am stuck on is: If I add an object to the NSMutableArray inside of the Person object, do I also have to turn that array of object (EventsObject) into NSData?
(I would assume I do, but I can't seem to figure out how to target that array of objects (EventsObject) to convert inside the PersonArray.
I hope I am explaining this in a manner that is easy to understand.
NSArray and some other classes implement the NSCoding protocol. So you can simplify your code to:
NSData *personData = [NSKeyedArchiver archivedDataWithRootObject:mutableDataArray];
NSUserDefaults *userData = [NSUserDefaults standardUserDefaults];
[userData setObject:personData forKey:#"personDataArray"];
[userData synchronize];
And for any objects to be archived you implement the NSCoding protocol. So for BC_Person it could look like this (I do not know the class so I am making up some properties as an example):
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.firstName forKey:#"firstName"];
[encoder encodeObject:self.lastName forKey:#"lastName"];
[encoder encodeObject:self.eventObjects forKey:#"eventObjects"];
}
In this example self.eventObjects would be an array of objects of your EventsObject class. The array knows how to encode itself, and for EventsObject you implement another -encodeWithCoder: method.
And to unarchive you reverse the process.

Possible to observe (KVO) every property of an NSObject?

I'm thingking of create a base class where every change made is immediately saved to NSUserDefaults (only KVO compilant parts of course), and automatically loads whenever that type of object is instantiated (a really basic, reusable user data store).
I have no intention to provide a "list of keys"-like constant to every subclass of this object, so I'm hoping that there is an automatic way to observe every property of an object.
Any ideas how to do this? With merely public API of course.
If you have a set of properties for an object, you can save them to a dictionary in NSUserDefaults. To save each property every time it is set you can create custom setter methods for each property using this:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"yourKey"] == nil) {
NSDictionary *dict = [NSDictionary dictionaryWithObject:#"yourObject" forKey:#"keyForYourObject"];
// Add stuff to the dictionary
[defaults setObject:dict forKey:#"yourKey"];
}else{
NSDictionary *dict = [defaults objectForKey:#"yourKey"];
// Add stuff to the dictionary
[defaults setObject:dict forKey:#"yourKey"];
}
This will give you a single dictionary stored in UserDefaults with all the properties. Then to get the list of keys use the standart method:
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] objectForKey:#"yourKey"];
NSArray *keys = [dict allKeys];

Resources