This question already has answers here:
How to store custom objects in NSUserDefaults
(7 answers)
Closed 9 years ago.
I created an object for a make believe user that stores the username and password, I then put the object in a dictionary. Now I'm trying to store the dictionary using NSUserDefault but its not letting me. I understand its best not to use NSUserDefault to store user data but this is just beginner stuff I'm trying to learn.
/**interface file **/
#import "newUser.h"
#interface BWViewController : UIViewController
{
IBOutlet UITextField *userNameField;
IBOutlet UITextField *passwordField;
IBOutlet UILabel * loginStatus;
}
#property (nonatomic, copy) NSDictionary * aUser;
-(IBAction)signUpButton;
#end
/**implementation file**/
#interface BWViewController ()
#end
#implementation BWViewController
#synthesize aUser;
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
-(IBAction) signUpButton
{
newUser * firstUser = [[newUser alloc] init];
firstUser.userName = userNameField.text;
firstUser.password = passwordField.text;
NSLog(#"%#", firstUser.userName); /**for testing**/
NSLog(#"%#", firstUser.password); /**for testing**/
aUser = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:firstUser, nil] forKeys:[NSArray arrayWithObjects:userNameField.text, nil]];
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:aUser forKey:userNameField.text];
[defaults synchronize];
loginStatus.text = #"Thanks for signing up!";
}
The better way to do that is store two user defaults one for username and one for password:
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:firstUser.userName forKey:#"USERNAME"];
[defaults setObject:firstUser.password forKey:#"PASSWORD"];
[defaults synchronize];
It doesn't work because you want to store nsdictionary with nsarray with custom object (firstUser) but NSUserDefaults doesn't allow you to store custom object.
Hope this help
//Extended
By saveForKey: and newUseFromKey: I mean something like that:
+(void)saveForKey:(NSString*)key
{
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:self.userName forKey:[NSString stringWithFormat:#"%#_USERNAME", key]];
[defaults setObject:self.password forKey:[NSString stringWithFormat:#"%#_PASSWORD", key]];
//.... other properties
[defaults synchronize];
}
+(newUser)newUseFromKey:(NSString*)key
{
newUser * user = [[newUser alloc] init];
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
user.userName = [defaults objectForKey:[NSString stringWithFormat:#"%#_USERNAME", key]];
user.password = [defaults objectForKey:[NSString stringWithFormat:#"%#_PASSWORD", key]];
//.... other properties
return user;
}
It's good solution if you don't store too many newUser object and if they don't have too many properties.
The best option is put all of your object to array/dictionary (It's up to you, there is no best option between this two) and save it to file. Just remember that if you want to save custom object to file system you have to implement NSCoding protocol.
Related
This question already has answers here:
Save images in NSUserDefaults?
(13 answers)
Closed 6 years ago.
In my project I need to save a custom object(Person) which have an UIImage, NSMutableArray and NSString objects. Currently I am able to encode and decode the NSString objects and archive Person to in NSUserDefault. Everything works fine except for UIImage and NSArray.
-(instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init]) {
self.userName = [aDecoder decodeObjectForKey:#"userName"]; //NSString
self.profileImage = [aDecoder decodeObjectForKey:#"profileImage"]; //UIImage
self.uplodedVideoArray = [aDecoder decodeObjectForKey:#"uplodedVideoArray"]; //NSMutableArray
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.userName forKey:#"userName"]; //NSString
[aCoder encodeObject:self.profileImage forKey:#"profileImage"]; //UIImage
[aCoder encodeObject:self.uplodedVideoArray forKey:#"uplodedVideoArray"]; //NSMutableArray
}
-(void)archivePerson
{
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:[Person sharedInstance]];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:encodedObject forKey:#"Person"];
[defaults synchronize];
}
-(Person *)unarchivePerson
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *encodedObject = [defaults objectForKey:#"Person"];
Person *person = [NSKeyedUnarchiver unarchiveObjectWithData:encodedObject];
return person;
}
You can only store certain types of objects to NSUserDefaults, the same types that can be written to plist files.
(See here https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/PropertyLists/AboutPropertyLists/AboutPropertyLists.html )
So you cannot store an image there. The reason you're having problems with the array is that collections (arrays/dictionaries) are only plist compatible if they contain plist compatible objects only. Your best approach is to save your image/video files seperately and then write the URL (as String/NSString) to NSUserDefaults. Best
I'm trying to save a NSMutableArray with NSUSerDefault and then open the array.
Here some code:
-(IBAction)btnSave{
Class *aClass = [[Class alloc]init];
aClass.idClass=#"aaaxxx";
aClass.nameClass=#"hello";
[myArray addObject:aClass];
NSUserDefaults *arrayDefault=[NSUserDefaults standardUserDefaults];
[arrayDefault setObject:myArray forKey:#"savedArray"];
[arrayDefault synchronize];
}
and
- (void)viewDidLoad
{
[super viewDidLoad];
myArray=[[NSMutableArray alloc]init];
NSMutableArray *savedArray=[[NSUserDefaults standardUserDefaults]objectForKey:#"savedArray"];
if(savedArray!=NULL){
myArray=savedArray;
[tableView reloadData];
}
// Do any additional setup after loading the view from its nib.
}
when I compile and when I push the button, this is what I read on log output:
[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value '(
"<Class: 0x8452b40>"
)' of class '__NSArrayM'. Note that dictionaries and arrays in property lists must also contain only property values
and obviously when I reopen the view the array is not loaded.
any help?
NSUserDefaults allows only Premitive DataTypes to be store in it. if you want to store your custom class Object then use following code, for more Detail refer this IOS Documentation
//create an array with your custom class objects
Class *aClass = [[Class alloc]init];
aClass.idClass=#"aaaxxx";
aClass.nameClass=#"hello";
[myArray addObject:aClass];
//convert your array to `NSData` object using `NSKeyedArchiver`
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:myArray];
//store it to `NSUserDefaults`
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"myArray"];
[[NSUserDefaults standardUserDefaults] synchronize];
//convert your stored Object back to `NSData` using `NSKeyedUnarchiver`
NSData *storedData = [[NSUserDefaults standardUserDefaults] objectForKey:#"myArray"];
NSArray *storedArr = [NSArray arrayWithArray:[NSKeyedUnarchiver unarchiveObjectWithData:storedData]];
NSLog(#"%#",storedArr[0]);
First you implement NSCoding in you object class. The in order to save/load the NSMutableArray of those objects you can do:
- (void) loadArray {
//Loading the NSMutableArray
NSData *arrayData = [[NSUserDefaults standardUserDefaults] objectForKey:#"ArrayKey"];
myArray = [NSKeyedUnarchiver arrayData];
}
-(void) saveArray {
//Saving the NSMutableArray
NSData *arrayData = [NSKeyedArchiver archivedDataWithRootObject:myArray];
[[NSUserDefaults standardUserDefaults] setObject:arrayData forKey:#"ArrayKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
For more info I suggest looking at a longer tutorial. For example Ray Wenderlich's How to Save your App Data with NSCoding And NSFileManager is a good one.
You need to implement NSCoding protocol in your model object Class. It is needed to serialize and deserialize non standard data to NSUserDefaults.
Now you can use NSKeyedArchiver to convert the object to data and store in userDefaults. Use NSKeyedUnarchiver to convert the data back to the object from defaults.
//h file
#interface Class : NSObject<NSCoding>
#property (nonatomic, copy) NSString *idClass;
#property (nonatomic, copy) NSString *nameClass;
//m File
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.idClass forKey:#"IDClass"];
[aCoder encodeObject:self.nameClass forKey:#"NameClass"];
}
- (id)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
if (self) {
self.idClass = [aDecoder decodeObjectForKey:#"IDClass"];
self.nameClass = [aDecoder decodeObjectForKey:#"NameClass"];
}
return self;
}
After implementing the NSCoding you can archive them to store as data in userDefaults
//Saving array
Class *aClass = [[Class alloc]init];
aClass.idClass=#"aaaxxx";
aClass.nameClass=#"hello";
[myArray addObject:aClass];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:myArray];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:data forKey:#"savedArray"];
[defaults synchronize];
//Retrieving
NSData *data = [defaults objectForKey:#"savedArray"];
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:data];
I save a custom object in NSUserDefults the object has more than one property ,
i initialize the value of the custom object in the AppDelegate ,
my problem is that when i try to retrieve the custom object property "userToken" in the viewcontroller didLoad method it works and the data is correct but when i retrieve it in another method in the view controller i get NULL note that the other property "uId" returns the correct value and i'm retrieving the objects in same way
what did i do wrong here ? help is really appreciated
here's how i save and retrieve the object
+(void)SaveUser:(User *)user
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSData *userData = [NSKeyedArchiver archivedDataWithRootObject:user];
[userDefaults setObject:userData forKey:#"User"];
[userDefaults synchronize];
}
+(User*)retrieveUser
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSData *userData = [ud objectForKey:#"User"];
User *user = [NSKeyedUnarchiver unarchiveObjectWithData:userData];
return user;
}
and here's the implementation of the User object
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if (self)
{
//uId and userToken are of type NSString
self.uId = [decoder decodeObjectForKey:#"UID"];
self.userToken = [decoder decodeObjectForKey:#"DeviceToken"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:self.uId forKey:#"UID"];
[encoder encodeObject:self.userToken forKey:#"DeviceToken"];
}
Try this :
Also note that + mean as static function that didn't have access to class properties make every thing in your saveuser or retrieveuser functions also decode without call another method out of class to decode it . that why maybe your + method get always null value when you use it out of loadview
+(void)SaveUser:(User *)user
{
NSUserDefaults *UserDefaults = [NSUserDefaults standardUserDefaults];
[UserDefaults setObject:[NSKeyedArchiver archivedDataWithRootObject:user] forKey:#"User"];
[UserDefaults synchronize];
}
+(User*)retrieveUser
{
NSUserDefaults *UserDefaults = [NSUserDefaults standardUserDefaults];
NSData *userData = [UserDefaults objectForKey:#"User"];
User *user = [NSKeyedUnarchiver unarchiveObjectWithData:userData];
return user;
}
When I replace the #"hello" with titlestring the table, the cell shows up blank, If i leave the hello, everything works, including the "detailsstring". I don't know why it's doing that since detailsstring is set up exactly the same as titlestring.
here is the code:
in the .m file I have
#synthesize detailsstring;
#synthesize titlestring;
and it's all defined the same in the .h in the .m I have this as well:
- (void)viewDidLoad{
titlestring =[[NSUserDefaults standardUserDefaults] objectForKey:#"titletext"];
detailsstring = [[NSUserDefaults standardUserDefaults] objectForKey:#"details"];
tabledata = [[NSArray alloc] initWithObjects:#"hello", nil];
tablesubtitles = [[NSArray alloc] initWithObjects:detailsstring, nil];
[super viewDidLoad];
}
Now, I'm saving those Userdefaults in another controller. Here is the save button action in a different view controller:
- (IBAction)saveButton:(id)sender {
NSUserDefaults *titletext = [NSUserDefaults standardUserDefaults];
[titletext setObject:titleTextfield.text forKey:#"titletext"];
NSUserDefaults *details = [NSUserDefaults standardUserDefaults];
[details setObject:detailstextfield.text forKey:#"details"];
NSUserDefaults *categoryUser = [NSUserDefaults standardUserDefaults];
[categoryUser setInteger:selectedCategory forKey:#"category"];
}
What am I doing wrong?
First of all, one instance of NSUserDefaults is sufficient for all three modifications. To 'push' the changes to the file system immediately, you can call [userDefaults synchronize];. Otherwise there is no guarantee that your changes are saved instantly. Apple suggests only to call this method if you cannot wait for the changes to be saved.
- (IBAction)saveButton:(id)sender {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:titleTextfield.text forKey:#"titletext"];
[userDefaults setObject:detailstextfield.text forKey:#"details"];
[userDefaults setInteger:selectedCategory forKey:#"category"];
[userDefaults synchronize];
}
In your viewDidLoad: method, try to output the saved values to test your application's integrity:
NSLog(#"%#", [[NSUserDefaults standardUserDefaults] objectForKey:#"details"]);
I am trying to save an object in NSUserDefaults but i have not been successful in it. Scenario is that, I have an object syncObject of class SyncObjct. I have implemented following methods in SyncObject class
- (NSMutableDictionary*)toDictionary;
+ (SyncObject*)getFromDictionary :(NSMutableDictionary*)dictionary;
- (id) initWithCoder: (NSCoder *)coder;
- (void) encodeWithCoder: (NSCoder *)coder;
And also the SyncObject class is NSCoding class. Now SyncObject class contains an NSMutableArray with name jobsArray. This array contains objects of class JobsObject. I implemented same above methods for this class as well to make it coding compliant. In toDictionary method of SyncObject class, i am storing this array in dictionary by writing following line.
[dictionary setValue:jobsArray forKey:#"jobsArray"];
and I am retrieving it in getFromDictionary method of SyncOject class by writing following line.
+ (SyncObject*)getFromDictionary :(NSMutableDictionary*)dictionary
{
NSLog(#"SyncObject:getFromDictionary");
SyncObject *syncObject = [[SyncObject alloc] init];
#try
{
syncObject.noOfJobs = [[dictionary valueForKey:#"noOfJobs"] intValue];
syncObject.totalJobsPerformed = (TotalJobsPerformed*)[dictionary valueForKey:#"totalobsPerformed"];
syncObject.jobsArray = [dictionary valueForKey:#"jobsArray"];
}
#catch (NSException * e)
{
NSLog(#"EXCEPTION %#: %#", [e name], [e reason]);
}
#finally
{
}
return syncObject;
}
And also I am storing syncObject in NSUserDefault by writing following lines.
NSData *myEncodedObject = [NSKeyedArchiver archivedDataWithRootObject:syncObject];
[userStorage setObject:myEncodedObject forKey:SYNC_OBJECT];
I am retrieving the object from NSUserDefaults by writing following lines
NSData *myEncodedObject = [userStorage objectForKey: SYNC_OBJECT];
syncObject = (SyncObject*)[NSKeyedUnarchiver unarchiveObjectWithData: myEncodedObject];
Problem is that i am not able to retrieve jobsArray correctly. Some invalid object is returned and app crashes when trying to access it. Please can anyone tell me the reason of this problem?
Best Regards
I believe the problem is that NSUserDefaults does not support storing of custom object types.
From Apple's documentation when using -setObject: The value parameter can be only property list objects: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. For NSArray and NSDictionary objects, their contents must be property list objects.
You can review the remaining documentation directly from Apple's NSUserDefault Class Reference manual.
Custom class .h
#import <Foundation/Foundation.h>
#interface Person : NSObject
#property(nonatomic, retain) NSArray *names;
#property(strong,nonatomic)NSString *name;
#end
custom class .m
#import "Person.h"
#implementation Person
#synthesize names;
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.name forKey:#"name"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if((self = [super init])) {
//decode properties, other class vars
self.name = [decoder decodeObjectForKey:#"name"];
}
return self;
}
#end
To create Person class object and store and retrieve your stored data from NSUserDefaults
Stored your object :
#import "ViewController.h"
#import "Person.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UITextField *textBox;
#end
NSMutableArray *arr;
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)saveButton:(id)sender {
Person *person =[Person new];
person.name = _textBox.text;
arr = [[NSMutableArray alloc]init];
[arr addObject:person];
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:arr];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:encodedObject forKey:#"name"];
[defaults synchronize];
}
- (IBAction)showButton:(id)sender {
_label.text = #"";
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *storedEncodedObject = [defaults objectForKey:#"name"];
NSArray *arrStoreObject = [NSKeyedUnarchiver unarchiveObjectWithData:storedEncodedObject];
for (int i=0; i<arrStoreObject.count; i++)
{
Person *storedObject = [arrStoreObject objectAtIndex:i];
_label.text = [NSString stringWithFormat:#"%# %#", _label.text , storedObject.name];
}
}
#end