Reputation: 209
I have a custom class which I store data in via NSUserDefaults. When I save one object into the array I can easily read it into my uitableview. It works perfectly. But as soon as there are more than 1 objects in the array I get the following error:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSConcreteMutableData 0x7f8adad3a630> valueForUndefinedKey:]: this class is not key value coding-compliant for the key fanLocation.'
The code for my class is the following:
#import <Foundation/Foundation.h>
@interface MotileFavorite : NSObject
@property (nonatomic, strong) NSString *favoriteName;
@property (nonatomic, strong) NSString *onNotificationsScreen;
@property (nonatomic, strong) NSString *fanLocation;
-(id)initWithName:(NSString *)favoriteName
withLocation:(NSString*)fanLocation
withIsOnNotificationsScreen:(NSString*)onNotificationsScreen;
@end
import "MotileFavorite.h"
@implementation MotileFavorite
-(id)initWithName:(NSString *)favoriteName withLocation:(NSString *)fanLocation withIsOnNotificationsScreen:(NSString *)onNotificationsScreen
{
self = [super init];
self.favoriteName = favoriteName;
self.fanLocation = fanLocation;
self.onNotificationsScreen = onNotificationsScreen;
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
//Encode properties, other class variables, etc
[encoder encodeObject:self.favoriteName forKey:@"favoriteName"];
[encoder encodeObject:self.fanLocation forKey:@"fanLocation"];
[encoder encodeObject:self.onNotificationsScreen forKey:@"onNotificationsScreen"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if((self = [super init])) {
//decode properties, other class vars
self.favoriteName = [decoder decodeObjectForKey:@"favoriteName"];
self.fanLocation = [decoder decodeObjectForKey:@"fanLocation"];
self.onNotificationsScreen = [decoder decodeObjectForKey:@"onNotificationsScreen"];
}
return self;
}
@end
And the code for the table view is this
#import <UIKit/UIKit.h>
@interface FavoritesListTableViewController : UITableViewController
@end
NSArray *favoritesArray;
NSArray *favoriteListNameArray;
NSArray *favoriteListLocationArray;
//the .m viewDidLoad is below where the error occurs
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.allowsMultipleSelectionDuringEditing = NO;
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults synchronize];
NSMutableArray *favoritesList = [defaults objectForKey:@"favoritesList"];
NSMutableArray *unArchivedFavoritesList = [NSMutableArray arrayWithCapacity:favoritesList.count];
for (NSData *mfavObject in favoritesList) {
NSData *mfUnEndocedObject = [NSKeyedUnarchiver unarchiveObjectWithData:mfavObject];
[unArchivedFavoritesList addObject:mfUnEndocedObject];
}
favoriteListLocationArray = [unArchivedFavoritesList valueForKey:@"fanLocation"];
favoriteListNameArray = [unArchivedFavoritesList valueForKey:@"favoriteName"];
}
The error occurs when trying to read to the favoriteListLocationArray. Here is the code when I save to the array:
- (IBAction)savePressed:(id)sender {
if (_NotificationsSegmentedControl.selectedSegmentIndex == 0){
_notificationBool = @"Yes";
}
else{
_notificationBool = @"No";
}
MotileFavorite *favorite = [[MotileFavorite alloc] initWithName:_nameFavoriteTextField.text withLocation:@"FANLOCATION" withIsOnNotificationsScreen:_notificationBool];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *favoritesList = [[NSMutableArray alloc] initWithArray: [defaults objectForKey:@"favoritesList"]];
[favoritesList addObject:favorite];
NSMutableArray *archiveArray = [NSMutableArray arrayWithCapacity:favoritesList.count];
for (MotileFavorite *mfavObject in favoritesList) {
NSData *mfEndocedObject = [NSKeyedArchiver archivedDataWithRootObject:mfavObject];
[archiveArray addObject:mfEndocedObject];
}
[defaults setObject:archiveArray forKey:@"favoritesList"];
[defaults synchronize];
}
I basically tried to do the "opposite" of saving it when I read it to the view but obviously that is not working for more than one value. Any idea what the code would be to make this work for multiple values in user defaults? Thanks!
Upvotes: 0
Views: 132
Reputation: 24572
You have to edit your MotileFavorite to have initWithCoder function to unarchive the data.
@implementation MotileFavorite
- (id)initWithCoder:(NSCoder *)coder;
{
if(self = [super init])
{
self.favoriteName = [coder decodeObjectForKey:@"favoriteName"];
self.fanLocation = [coder decodeObjectForKey:@"fanLocation"];
self.onNotificationsScreen = [coder decodeObjectForKey:@"fanLocation"];
}
return self;
}
@end
Then do this to archive and unarchive it.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
MotileFavorite *m1 = [[MotileFavorite alloc] initWithName:@"A2"
withLocation:@"A"
withIsOnNotificationsScreen:@"A"];
MotileFavorite *m2= [[MotileFavorite alloc] initWithName:@"A1"
withLocation:@"A"
withIsOnNotificationsScreen:@"A"];
NSArray *array = [[NSArray alloc] initWithObjects:m1,m2, nil];
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver
archivedDataWithRootObject:array]
forKey:@"favoritesList"];
[defaults synchronize];
NSData *favouriteData = [defaults objectForKey:@"favoritesList"];
NSArray *unArchivedFavoritesList = [NSKeyedUnarchiver unarchiveObjectWithData:favouriteData];
favoriteListLocationArray = [unArchivedFavoritesList valueForKey:@"fanLocation"];
favoriteListNameArray = [unArchivedFavoritesList valueForKey:@"favoriteName"];
[NSKeyedArchiver archivedDataWithRootObject:]
generates an NSData object from the array. Save it in NSUserDefaults.
initWithCoder
does the reverse of the encodeWithCoder function, to get back the object.
[NSKeyedUnarchiver unarchiveObjectWithData:]
get the array back from NSData
.
Upvotes: 0
Reputation: 534904
The error message is quite clear. You are sending valueForKey:
to an NSData object. You can't do that.
Let's look at your code to see how that is happening. According to your own code, you are creating unArchivedFavoritesList
as an NSArray of NSData objects:
NSData *mfUnEndocedObject =
[NSKeyedUnarchiver unarchiveObjectWithData:mfavObject];
[unArchivedFavoritesList addObject:mfUnEndocedObject];
Subsequently, you are calling valueForKey:
on that array:
[unArchivedFavoritesList valueForKey:@"fanLocation"];
valueForKey:
sent to an NSArray sends valueForKey:
to each of the array's contained objects. But NSData objects do not respond to valueForKey:
, so that makes no sense - and we crash.
EDIT: The reason this is happening turns out to be that you are archiving your objects before putting them into the array. That's not how archiving works. Archiving archives an object graph. You make your objects archivable and put the objects themselves into the array. Then you archive the array! The whole graph - the array and the objects - is archived for you. Now you have a reversible operation.
Upvotes: 1