I have been trying to save locally (through NSUserDefaults) a NSMutableArray of PFGeoPoints that a user creates. After much reading and research, I now know that simply saving an array with custom data is quite frankly impossible. I have tried the following in several different ways:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *testArray = [[NSMutableArray alloc] init];
NSArray *checkArray = [[NSArray alloc] init];
testArray = locationList;
PFGeoPoint *test = [locationList objectAtIndex:0];
NSString *latString = [NSString stringWithFormat:#"%f",test.latitude];
[testArray insertObject:latString atIndex:0];
checkArray = testArray;
[defaults setValue:checkArray forKey:#"Locations"];
[defaults synchronize];
where locationList is a NSMutableArray that has been created and set to PFGeoPoints from Parse.
I had also tried converting the NSMutableArray to NSData via
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
, but this still did not work. Each time I kept receiving the error telling me the following:
Property list invalid for format: 200
(property lists cannot contain objects of type 'CFType')
The NSMutableArray that I am saving is a list that contains multiple PFGeoPoints. All I am trying to do is save this array to the device, so that the user does not have to recreate (render) the entire list each time the user loads the application (I am using cloud data...not trying to spend too much for data).
If someone could help me out with either a suggestion/solution on how to save this data as an array (rather than breaking up each line into integers for lat and long), OR if someone can suggest a helpful alternative to NSUserDefaults (like perhaps Core Data or some other local storage) I would be most appreciative. Thanks
simply you need to implement NSCoding protocol
Write array:
Obj-C
-(void)writeArrayWithCustomObjToUserDefaults:(NSString *)keyName withArray:(NSMutableArray *)myArray {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:myArray];
[defaults setObject:data forKey:keyName];
[defaults synchronize];
}
Swift 2.3
func writeArrayWithCustomObjToUserDefaults(keyName: String, withArray myArray: [AnyObject]) {
var defaults = NSUserDefaults.standardUserDefaults()
var data = NSKeyedArchiver.archivedDataWithRootObject(myArray)
defaults.setObject(data, forKey: keyName)
defaults.synchronize()
}
Swift 3.1
func writeArrayWithCustomObj(toUserDefaults keyName: String, withArray myArray: [Any]) {
let defaults = UserDefaults.standard
let data = NSKeyedArchiver.archivedData(withRootObject: myArray)
defaults.set(data, forKey: keyName)
defaults.synchronize()
}
Read Array:
Obj-C
-(NSArray *)readArrayWithCustomObjFromUserDefaults:(NSString*)keyName {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:keyName];
NSArray *myArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
[defaults synchronize];
return myArray;
}
Swift 2.3
func readArrayWithCustomObjFromUserDefaults(keyName: String) -> [AnyObject] {
var defaults = NSUserDefaults.standardUserDefaults()
var data = defaults.objectForKey(keyName)!
var myArray = NSKeyedUnarchiver.unarchiveObjectWithData(data)!
defaults.synchronize()
return myArray
}
Swift 3.1
func readArrayWithCustomObj(fromUserDefaults keyName: String) -> [Any] {
let defaults = UserDefaults.standard
let data: Data? = defaults.object(forKey: keyName)
let myArray: [Any]? = NSKeyedUnarchiver.unarchiveObject(with: data!)
defaults.synchronize()
return myArray!
}
To save custom objects into user defaults, it need to conform to NSCoding protocol (objects needs to code/decode).
Your custom Objects need to implement the NSCoding protocol.
Once you followed NSCoding rules; archive & save data like this:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:yourArray];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"Key"];
Unarchive like this:
NSArray *array= [NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:#"Key"];
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
My application saving NSData (contains bookmarked file reference) list to NSUserDefaults in somewhat following way, at any point of application process:
NSMutableArray *bookmarkedURLs = [[NSMutableArray alloc] init];
[bookmarkedURLs addObject:bookmark]; // 'bookmark' is a NSData object
[[NSUserDefaults standardUserDefaults] setObject:bookmarkedURLs forKey:#"AppBookmarks"];
[[NSUserDefaults standardUserDefaults] synchronize];
When application starts I checked through NSUserDefaults to populate saved NSData list:
bookmarkedURLs = [[[NSUserDefaults standardUserDefaults] objectForKey:#"AppBookmarks"] mutableCopy];
if (bookmarkedURLs.count == 0)
{
bookmarkedURLs = [[NSMutableArray alloc] init];
NSLog(#"INITIALIZED");
}
else
{
NSLog(#"STORES NSDATA LIST");
....
}
The problem I faced I can order in following steps:
There is no saved NSUserDefaults data by 'AppBookmarks' key
Save a NSData to NSUserDefaults by 'AppBookmarks' key
Restarts application
Application tries to populate a NSMutableArray from NSUserDefaults' 'AppBookmarks' key but always found 0 records
I save again a new NSData to 'AppBookmarks' key
Restarts application
Application tries to populate NSMutableArray from 'AppBookmarks' and this time it found saved record(s).
Any restart of application or new NSData addition to 'AppBookmarks' never fails thereafter
So whenever there is no saved data to NSUserDefaults and I saved a value, it's not loading or saving for first time. Any attempt to save and load is working after then.
Try this:
NSMutableArray *bookmarkedURLs = [[NSMutableArray alloc] init];
[bookmarkedURLs addObject:bookmark]; // 'bookmark' is a NSURL object
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:bookmarkedURLs forKey:#"AppBookmarks"];
[defaults synchronize];
I hope this can help you.
You can do something like,
NSUserDefaults *myDefaults = [NSUserDefaults standardUserDefaults];
// for example
NSURL *url1; // your url1
NSURL *url2; // your url2
//Set data to userdefaults
NSMutableArray *arr = [[NSMutableArray alloc]initWithObjects:url1,url2, nil]; // Store urls directly in array
NSData *bookMarkdata = [NSKeyedArchiver archivedDataWithRootObject:arr]; // convert whole array in data
[myDefaults setObject:bookMarkdata forKey:#"AppBookmarks"]; // save that data object to userdefaults
[myDefaults synchronize];
//Retreive data from userdefaults
NSData *resultData = [myDefaults objectForKey:#"AppBookmarks"]; //retrieve data from user defaults
NSMutableArray *resultArr = [NSKeyedUnarchiver unarchiveObjectWithData:resultData]; // get result array from data
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];
Currently I'm trying to save an NSRect to my user defaults. Right now I'm using a NSValue to wrap it as an object then save it into the user defaults as an object with the following code
[userDefaults setObject:[NSValue valueWithRect:rect forKey:#"Key"]];
However, when I run it, I get an error that says [NSUserDefaults setObject:forKey:]: Attempt to insert non-property value 'NSRect: {{0, 0}, {50, 50}}' of class 'NSConcreteValue'.
Does anyone know how I can fix this? Thanks.
Here is a much simpler solution, using NSString:
// store:
[[NSUserDefaults standardUserDefaults]
setValue:NSStringFromCGRect(frame) forKey:#"frame"];
// retrieve:
CGRect frame = CGRectFromString(
[[NSUserDefaults standardUserDefaults] stringForKey:#"frame"]);
Swift 4
// store
UserDefaults.standard.set(NSStringFromCGRect(rect), forKey: "frame")
// retrieve
if let s = UserDefaults.standard.string(forKey: "frame") {
let frame = CGRectFromString(s)
}
NSValue is not supported by the user defaults.
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 need to archive the value into data then unarchive it when you access it.
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSRect rect = { 0, 0, 10, 20 };
NSValue *value = [NSValue valueWithRect:rect];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:value];
[userDefaults setObject:data forKey:#"key"];
[userDefaults synchronize];
data = [userDefaults objectForKey:#"key"];
NSValue *unarchived = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"%#", unarchived);
Can you show me the syntax or any sample programs to archive an NSArray of custom objects in Objective-C?
Check out NSUserDefaults.
For Archiving your array, you can use the following code:
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:myArray] forKey:#"mySavedArray"];
And then for loading the custom objects in the array you can use this code:
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *savedArray = [currentDefaults objectForKey:#"mySavedArray"];
if (savedArray != nil)
{
NSArray *oldArray = [NSKeyedUnarchiver unarchiveObjectWithData:savedArray];
if (oldArray != nil) {
customObjectArray = [[NSMutableArray alloc] initWithArray:oldSavedArray];
} else {
customObjectArray = [[NSMutableArray alloc] init];
}
}
Make sure you check that the data returned from the user defaults is not nil, because that may crash your app.
The other thing you will need to do is to make your custom object to comply to the NSCoder protocol. You could do this using the -(void)encodeWithCoder:(NSCoder *)coder and -(id)initWithCoder:(NSCoder *)coder methods.
If you want to save to a file (rather than using NSUserDefaults) you can use -initWithContentsOfFile: to load, and -writeToFile:atomically: to save, using NSArrays.
Example:
- (NSArray *)loadMyArray
{
NSArray *arr = [NSArray arrayWithContentsOfFile:
[NSString stringWithFormat:#"%#/myArrayFile", NSHomeDirectory()]];
return arr;
}
// returns success flag
- (BOOL)saveMyArray:(NSArray *)myArray
{
BOOL success = [myArray writeToFile:
[NSString stringWithFormat:#"%#/myArrayFile", NSHomeDirectory()]];
return success;
}
There's a lot of examples on various ways to do this here: http://www.cocoacast.com/?q=node/167