How to save NSMutableArray of "custom class objects" in NSUserDefaults? [duplicate] - ios

This question already has answers here:
How to save custom objects in array and store it in NSUserDefaults - iPhone
(3 answers)
Closed 5 years ago.
I've seen a few posts about how you can't save an object based NSMutableArray in NSUserDefaults, but that there is a way of doing it using [NSKeyedArchiver] and encoding it with NSCoder although I'm struggling to understand how to do it and was hoping someone could help.
Student *student1 = [Student studentWithImage:[UIImage imageNamed:#"default"] withForename:#"John" withSurname:#"Smith" withAddress:#"3 Fake Road, Faketown, FA31 KEE" withDateOfBirth:[dateFormatter dateFromString:#"17-Jul-93"] withAge:24];
[studentArray addObject:student1];
I want this and a few other similar Students to be saved to my NSMutableArray studentArray. I want to then save this array to NSUserDefaults and load it back up again.
I have seen this post here which looks like is the correct answer to my question, but I need help implementing it to my code as I'm having difficulty understanding it!
Thanks

First add this in Student class
- (void)encodeWithCoder:(NSCoder *)coder;
{
[coder encodeObject:self.Forename forKey:#"keyForename"];
// do same for all property
}
- (id)initWithCoder:(NSCoder *)coder;
{
self = [[Student alloc] init];
if (self != nil)
{
self.Forename = [coder decodeObjectForKey:#"keyForename"];
// do same other property here
}
return self;
}
Store Array
[studentArray addObject:student1];
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:studentArray] forKey:#"mySavedArray"];
Retrieve Array
NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *savedArray = [currentDefaults objectForKey:#"mySavedArray"];
if (savedArray != nil)
{
NSArray *oldArray = [NSKeyedUnarchiver unarchiveObjectWithData:savedArray];
if (oldArray != nil) {
customObjectArray = [[NSMutableArray alloc] initWithArray:oldArray];
} else {
customObjectArray = [[NSMutableArray alloc] init];
}
}

Related

convert NSObject to NSDictionary [duplicate]

This question already has answers here:
Obj-C easy method to convert from NSObject with properties to NSDictionary?
(6 answers)
Closed 6 years ago.
How do i to convert NSObject to NSDictionary?
At first step i have converted NSDictionary to NSObject like,
QRCodeData *obj = [[QRCodeData alloc] initWithQRcodeData:myDictonary];
QRCodeData.h
#interface QRCodeData : NSObject
-(instancetype)initWithQRcodeData:(NSDictionary*)dictionary;
#end
QRCodeData.m
#implementation QRCodeData
-(instancetype)initWithQRcodeData:(NSDictionary*)dictionary
{
self = [super init];
if(self){
self.name = dictionary[#"userName"];
self.phoneNumber = dictionary[#"mobileNo"];
}
return self;
}
#end
I want my Dictionary from object, it is possible to get?
Please help and thanks in advance..
you can simply get dictionary like,
NSDictionary *dict = #{#"userName": obj.name ,#"mobileNo" : obj.phoneNumber };
here obj is QRCodeData's object.
Hope this will help :)
Easiest way is to add this method in the QRCodeData class.
- (NSDictionary *)dictionaryValue
{
return #{#"userName" : self.name, #"mobileNo" : self.phoneNumber};
}
If userName and phoneNumber could be nil you have to check that.
To call with
NSDictionary *dict = [obj dictionaryValue];
You can use Key-Value Coding(KVC) for this purpose. First, provide class method for all keys you want to share:
+ (NSSet *)keysToCopy
{
return [NSSet setWithObjects:#"userName", #"mobileNio", .....];
}
Then you can do something like in your init method:
for (key in [[self class] keysToCopy])
{
[self setValue:dictionary[key] forKey:key];
}
and provide another method to revert it back to NSDictionary:
- (NSDictionary *)dictionaryRepresentation
{
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (key in [[self class] keysToCopy])
{
[result setObject:[self valueForKey:key] forKey:key];
}
}
The only problem remains that not every property is compatible to NSDictionary storing.
This approach allows you to scale this solution to any Cocoa object and it doesn't require you to change anything but keysToCopy method in case if there are new properties to share.

Saving non-property list items to NSUserDefaults

I have an array of custom objects which I want to store in array that updates the UITableView. The array is quite small ~10 objects at most. However, I discovered that I am unable to save an array of custom objects because:
Property list invalid for format: 200 (property lists cannot contain objects of type 'CFType')
2015-01-04 17:56:33.414 fanSyncDemo[13985:1264828] Attempt to set a non-property-list object (
It looks like I am going to have to turn the objects into NSData objects using NSKeyArchiver. However, I am having trouble understanding how this will look. Could someone help me with an example? My code for the saving looks like this:
- (IBAction)savePressed:(id)sender {
if (_NotificationsSegmentedControl.selectedSegmentIndex == 0){
_notificationBool = #"Yes";
}
else{
_notificationBool = #"No";
}
MyFavorite *favorite = [[MyFavorite alloc] initWithName:_nameFavoriteTextField.text withLocation:#"FANLOCATION" withIsOnNotificationsScreen:_notificationBool];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *favoritesList = [[NSMutableArray alloc] initWithArray: [defaults objectForKey:#"favoritesList"]];
[favoritesList addObject:favorite];
[defaults setObject:favoritesList forKey:#"favoritesList"];
[defaults synchronize];
}
This is not a duplicate of this question because that example doesn't explain how to retrieve the data, nor can I figure out how to apply it to my example which normally I don't have a problem with but I have just been struggling with this.
From what I understand your going to want to do something like this in your MyFavorite class
- (id)initWithCoder:(NSCoder *)aDecoder
{
_location = [aDecoder decodeObjectForKey:#"location"];
_isOnNotificationsScreen = [aDecoder decodeObjectForKey:#"notificationScreen"];
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.location forKey:#"location"];
[aCoder encodeObject:self.isOnNotificationsScreen forKey:#"notificationScreen"];
}

Save data even if the app re-opens - Swift Xcode 6 iOS

I'm working on a app that can randomize love couples. Just a fun thing, okey!?!? :D
But the problem, or maybe not a problem but a thing that can be much better if I get this thing to be working. In the beginning you need to write in all the names. And thats takes some time... Should I use Core Date? I don't really knows what core data is so I'm not sure. I would love if a god come to me and wrote the full code that can remember an array even if the app and phone shuts down. I have done this in java, is that simpel that it is in java? That would be great!
//Thank, Anton
For Heavy, complex data structures you would want to use core data,
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdTechnologyOverview.html#//apple_ref/doc/uid/TP40009296-SW1
But seeing as you just want to store an array, You should look into NSUserDefaults.
NSUserDefaults will store given data as long as the app is not deleted. You will most likely want to create some kind of custom DataStorage class for this.
#interface DataStorage : NSObject <NSCoding>
#property (nonatomic, strong) NSMutableArray *arrayToStore;
+ (instancetype)sharedInstance;
- (void)save;
#end
Above is the .h file. As you can see, it follows NSCoding protocols. That provides access to methods which allow you to encode data. You will use the save method to write the data to disk.
#import "DataStorage.h"
#implementation DataStorage
#synthesize arrayOfPeople = _arrayToStore;
+ (DataStorage *)sharedInstance
{
static DataStorage *state = nil;
if ( !state )
{
NSData *data =[[NSUserDefaults standardUserDefaults] objectForKey:#"DataStorageKey"];
if (data)
{
state = [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
else
{
state = [[DataStorage alloc] init];
}
}
return state;
}
- (id)init{
if (self = [super init]) {
if (!_arrayToStore) {
_arrayToStore = [[NSMutableArray alloc] init];
}
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)decoder
{
self = [self init];
if (self) {
if ([decoder decodeObjectForKey:#"DataStorageArrayToStore"]) {
_arrayToStore = [[decoder decodeObjectForKey:#"DataStorageArrayToStore"] mutableCopy];
}
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:_arrayToStore forKey:#"DataStorageArrayToStore"];
}
- (void)save
{
NSData *appStateData = [NSKeyedArchiver archivedDataWithRootObject:self];
[[NSUserDefaults standardUserDefaults] setObject:appStateData forKey:#"DataStorageKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
#end
Here is the .m file, which pretty much evaluates to see if there is a saved instance of the class, and if not it will create one. [DataStorage sharedInstance]...
when you want to store some data, you will simply make the class available to said file, #import "DataStorage.m and then use
NSString *testData = [NSString stringWithFormat: #"Test Data String"];
[[DataStorage sharedInstance].arrayToStore addObject: testData];
[DataStorage sharedInstance] save];

Init array from another without modifying its content

I need to make a "clone" of an array to another, but the thing is that when I modify my copied Array, the original is modified too. Using hard copy is not working as I expect.
I'm initializing an array like this:
NSMutableArray *otherArray = [[NSMutableArray alloc] initWithArray: myList copyItems:YES];
where myList is a NSArray that came as a parameter in my function.
The thing is when I need to return myList, it's content has been modified when I modify my otherArray
I tried making a hard copy like:
NSMutableArray* algo = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:myList]];
But, some of the properties are not converted, and a nil value is assigned to them.
Also I tried with this:
NSMutableArray *otherArray = [myList mutableCopy];
Well, the obvious question is: How can I modify a copied object without modifying the original one?
Thanks!
EDIT: Here is my entire function.
RAC(self,filteredPacks) = [RACSignal combineLatest:#[self.searchBoxSignal, self.packListsSignal]
reduce:^NSArray *(NSString *filterString, NSArray *packList) {
NSMutableArray *sweetHelper = [[NSMutableArray alloc] init];
NSMutableArray* packListCopy = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject:packList]];
filterString = [filterString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([filterString length] > 0)
{
for(PackList *theList in packListCopy){
NSMutableIndexSet *indexesToDelete = [NSMutableIndexSet indexSet];
NSUInteger currentIndex = 0;
for(Pack *thePack in theList.resolved_packs){
if([thePack.name rangeOfString:filterString options:NSCaseInsensitiveSearch].location == NSNotFound){
[indexesToDelete addIndex:currentIndex];
}
currentIndex++;
}
[theList.resolved_packs removeObjectsAtIndexes:indexesToDelete];
[theList.packs removeObjectsAtIndexes:indexesToDelete];
[sweetHelper addObject:theList];
}
return sweetHelper;
}
else
{
return self.originalList;
}
}
];
Well, I found the solution using this answer and this comment in the same question.
As I said in my question:
"I tried making a hard copy like:
NSMutableArray* algo = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:myList]];
But, some of the properties are not converted, and a nil value is assigned to them."
Well, the properties that were not converted, they weren't because they are custom objects, An I didn't implemented the initWithCode and encodeWithCoder methods. So, when I added those methods to my customObjectClass, my Arrays were hard copied with all their elements.
A little example about what I did:
In my SomeCustomObject.h I should implements NSCoding:
#interface SomeCustomObject : NSObject <NSCoding> {
NSMutableArray * __packs;
int __type;
Link * __selfRef;
NSMutableArray * __resolved_packs;
}
And in my subclass of SomeCustomObject I got something like:
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
__packs = [decoder decodeObjectForKey:#"packs"];
__type = [decoder decodeIntForKey:#"type"];
__selfRef = [decoder decodeObjectForKey:#"selfRef"];
__resolved_packs = [decoder decodeObjectForKey:#"resolved_packs"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:__packs forKey:#"packs"];
[encoder encodeInt:__type forKey:#"type"];
[encoder encodeObject:__selfRef forKey:#"selfRef"];
[encoder encodeObject:__resolved_packs forKey:#"resolved_packs"];
}
I hope this be useful to somebody :)

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

Resources