NSUserdefaults not working with NSKeyedArchiver

218 views Asked by At

I have a NSMutableArray filled with objects of my Movie class wich i want to save but it doesn't work and i can not figure out why...

Movie.h:

@interface Movie : NSObject <NSCoding>{
    NSString *name;
    int year;
    int length;
    NSString *file_size;
    int rating;
    NSArray *genre;
    NSString *plot;
}

@property (nonatomic, retain) NSString *name;
@property (nonatomic, assign) int year;
@property (nonatomic, assign) int length;
@property (nonatomic, retain, retain) NSString *file_size;
@property (nonatomic, assign) int rating;
@property (nonatomic, retain) NSArray *genre;
@property (nonatomic, retain) NSString *plot;

-(id) initWithName:(NSString*)newName year:(int)newYear length:(int)newLength filesize:(NSString*)newFileSize rating:(int)newRating genre:(NSArray*)newGenre plot:(NSString*)newPlot;
- (void) encodeWithCoder : (NSCoder *)encode ;
- (id) initWithCoder : (NSCoder *)decode;

@end

Movie.m:

@implementation Movie

@synthesize name;
@synthesize year;
@synthesize length;
@synthesize file_size;
@synthesize rating;
@synthesize genre;
@synthesize plot;

-(id)initWithName:(NSString *)newName year:(int)newYear length:(int)newLength filesize:(NSString *)newFileSize rating:(int)newRating genre:(NSArray *)newGenre plot:(NSString *)newPlot{
    self.name = newName;
    self.year = newYear;
    self.length = newLength;
    self.file_size = newFileSize;
    self.rating = newRating;
    self.genre = newGenre;
    self.plot = newPlot;
    return self;
}
- (void)encodeWithCoder:(NSCoder *)encode;
{
    [encode encodeObject:name forKey:@"name"];
    [encode encodeInt32:year forKey:@"year"];
    [encode encodeInt32:length forKey:@"length"];
    [encode encodeObject:file_size forKey:@"file_size"];
    [encode encodeInt32:rating forKey:@"rating"];
    [encode encodeObject:genre forKey:@"genre"];
    [encode encodeObject:plot forKey:@"plot"];
}

- (id)initWithCoder:(NSCoder *)decode;
{
        NSString *name_decode = [decode decodeObjectForKey:@"name"];
        int year_decode = [decode decodeInt32ForKey:@"year"];
        int length_decode = [decode decodeInt32ForKey:@"length"];
    NSString *file_size_decode = [decode decodeObjectForKey:@"file_size"];
    int rating_decode = [decode decodeInt32ForKey:@"rating"];
    NSArray *genre_decode = [decode decodeObjectForKey:@"genre"];
    NSString *plot_decode =[decode decodeObjectForKey:@"plot"];
    return [self initWithName:name_decode year:year_decode length:length_decode filesize:file_size_decode rating:rating_decode genre:genre_decode plot:plot_decode];
}

@end

Save Action (Movies is the NSMutableArray containing my Objects):

NSUserDefaults *userDefault=[NSUserDefaults standardUserDefaults];
    NSData *encodedData = [NSKeyedArchiver archivedDataWithRootObject:Movies];
    [userDefault setObject:encodedData forKey:[NSString stringWithFormat:@"MOVIES"]];

Load Action:

NSData *decodedData = [userDefault objectForKey: [NSString stringWithFormat:@"MOVIES"]];
    NSArray *decodedArray =[NSKeyedUnarchiver unarchiveObjectWithData: decodedData];

The returned Array is always (null)... i have no clue I tried several different kind of code snippets i found on the internet and/or stackoverflow

2

There are 2 answers

1
rmaddy On BEST ANSWER

Your Movie initWithName... method is incorrect. It needs to be:

- (instancetype)initWithName:(NSString *)newName year:(int)newYear length:(int)newLength filesize:(NSString *)newFileSize rating:(int)newRating genre:(NSArray *)newGenre plot:(NSString *)newPlot {
    self = [super init];
    if (self) {
        self.name = newName;
        self.year = newYear;
        self.length = newLength;
        self.file_size = newFileSize;
        self.rating = newRating;
        self.genre = newGenre;
        self.plot = newPlot;
    }

    return self;
}

Also, you seem to be following a very out-of-date tutorial.

  1. You don't need to declare the ivars for your properties.
  2. You don't need the calls to @synthesize.
  3. You should be using ARC instead of MRC, Therefore your retain properties should be strong (thought the NSString properties should be copy.
  4. Your init methods should return instancetype, not id.

With all of that in mind, your Movie class should be as follows:

Movie.h

@interface Movie : NSObject <NSCoding>

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int year;
@property (nonatomic, assign) int length;
@property (nonatomic, copy) NSString *file_size;
@property (nonatomic, assign) int rating;
@property (nonatomic, strong) NSArray *genre;
@property (nonatomic, copy) NSString *plot;

- (instancetype)initWithName:(NSString *)newName year:(int)newYear length:(int)newLength filesize:(NSString *)newFileSize rating:(int)newRating genre:(NSArray *)newGenre plot:(NSString *)newPlot;

@end

Movie.m

@implementation Movie

- (instancetype)initWithName:(NSString *)newName year:(int)newYear length:(int)newLength filesize:(NSString *)newFileSize rating:(int)newRating genre:(NSArray *)newGenre plot:(NSString *)newPlot {
    self = [super init];
    if (self) {
        _name = newName;
        _year = newYear;
        _length = newLength;
        _file_size = newFileSize;
        _rating = newRating;
        _genre = newGenre;
        _plot = newPlot;
    }

    return self;
}

- (void)encodeWithCoder:(NSCoder *)encode;
{
    [encode encodeObject:self.name forKey:@"name"];
    [encode encodeInt32:self.year forKey:@"year"];
    [encode encodeInt32:self.length forKey:@"length"];
    [encode encodeObject:self.file_size forKey:@"file_size"];
    [encode encodeInt32:self.rating forKey:@"rating"];
    [encode encodeObject:self.genre forKey:@"genre"];
    [encode encodeObject:self.plot forKey:@"plot"];
}

- (instancetype)initWithCoder:(NSCoder *)decode;
{
    NSString *name_decode = [decode decodeObjectForKey:@"name"];
    int year_decode = [decode decodeInt32ForKey:@"year"];
    int length_decode = [decode decodeInt32ForKey:@"length"];
    NSString *file_size_decode = [decode decodeObjectForKey:@"file_size"];
    int rating_decode = [decode decodeInt32ForKey:@"rating"];
    NSArray *genre_decode = [decode decodeObjectForKey:@"genre"];
    NSString *plot_decode =[decode decodeObjectForKey:@"plot"];
    return [self initWithName:name_decode year:year_decode length:length_decode filesize:file_size_decode rating:rating_decode genre:genre_decode plot:plot_decode];
}

@end

You don't show how you create and populate your Movies variable (which should be named movies, not Movies. Make sure it isn't nil.

Also, don't needlessly use stringWithFormat.

Your saving code should be:

NSUserDefaults *userDefault=[NSUserDefaults standardUserDefaults];
NSData *encodedData = [NSKeyedArchiver archivedDataWithRootObject:movies];
[userDefault setObject:encodedData forKey:@"MOVIES"];

and your loading code should be:

NSData *decodedData = [userDefault objectForKey:@"MOVIES"];
NSArray *decodedArray = [NSKeyedUnarchiver unarchiveObjectWithData: decodedData];
0
dennykim On

A quick test is to see if the mutable array is actually not nil itself. Try outputting the mutable array before setting it in userDefaults.

Make sure the mutable array is initialized before trying to add objects to it.

movies = [[NSMutableArray alloc] init];