wayneh
wayneh

Reputation: 4413

How to Create a Class(es) for storing multi-level data?

I need to store multi-level data for a flash card app I'm writing and I could use some help figuring out 1) How to manage the data and 2) How to store it.

The data is broken down like this: a) Card contains 2 strings b) Pack contains a String "PackName" and an array of Cards c) Deck contains a String "DeckName" and an array of Packs

Right now I have 3 Classes: Card, Pack, Deck.

//Card.h
@interface Card : NSObject {
    NSString        *primaryPhrase;
    NSString        *secondaryPhrase;
}
@property (nonatomic,retain)NSString        *primaryPhrase;
@property (nonatomic,retain)NSString        *secondaryPhrase;
@end


Card.m
@implementation Card
@synthesize primaryPhrase;
@synthesize secondaryPhrase;
-(id)init{
    if(self=[super init]){
    }
    return self;
}
@end

Pack.h
@interface Pack : NSObject{
    NSString        *packName;
    NSMutableArray  *cards; //array of card classes
    BOOL            isInUse;
}
@property (nonatomic,retain)NSMutableArray  *cards;
@property (nonatomic,retain)NSString        *packName;
@property (nonatomic,assign)BOOL            isInUse;
@end

Pack.m
@implementation Pack
@synthesize packName;
@synthesize cards;
@synthesize isInUse;
-(id)init{
    if(self=[super init]){
        self.isInUse=YES;
    }
    return self;
}
@end

Deck.h
@interface Deck : NSObject <NSCoding>{
    NSString        *deckName;
    NSMutableArray  *packs; //array of pack classes
    NSString        *primaryLang;
    NSString        *secondaryLang;
}
@property (nonatomic,retain)NSMutableArray  *packs;
@property (nonatomic,retain)NSString        *deckName;
@property (nonatomic,retain)NSString        *primaryLang;
@property (nonatomic,retain)NSString        *secondaryLang;

- (void) encodeWithCoder:(NSCoder*)encoder;
- (id) initWithCoder:(NSCoder*)decoder;
@end

Deck.m
#import "Deck.h"
@implementation Deck
@synthesize packs;
@synthesize deckName;
@synthesize primaryLang;
@synthesize secondaryLang;

//Default settings for each new Deck
-(id)init{
    if(self=[super init]){
    }
    return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder{
    [encoder encodeObject:packs forKey:@"packs"];
    [encoder encodeObject:deckName forKey:@"deckName"];
    [encoder encodeObject:primaryLang forKey:@"primaryLang"];
    [encoder encodeObject:secondaryLang forKey:@"secondaryLang"];
}
-(id)initWithCoder:(NSCoder*)decoder{
    if(self=[super init]){
        packs=[decoder decodeObjectForKey:@"packs"];
        deckName=[decoder decodeObjectForKey:@"deckName"];
        primaryLang=[decoder decodeObjectForKey:@"primaryLang"];
        secondaryLang=[decoder decodeObjectForKey:@"secondaryLang"];
    }
    return self;
}
@end

Then I use an NSMutableArray "allDecks" to hold Decks, which in turn contain Cards, but I haven't even been able to get this to work (no errors, but "pack name" is always null):

    for(int i=0; i<=2; i++){
        Deck *newdeck=[[Deck alloc]init];
        [globDat.allDecks addObject:newdeck];
    }
    ((Deck *)[globDat.allDecks objectAtIndex:0]).deckName=@"DeckName 0";
    ((Deck *)[globDat.allDecks objectAtIndex:1]).deckName=@"DeckName 1";
    ((Deck *)[globDat.allDecks objectAtIndex:2]).deckName=@"DeckName 2";
    for(int i=0; i<=2; i++){
        Pack *newpack=[[Pack alloc] init];
        [((Deck *)[globDat.allDecks objectAtIndex:i]).packs addObject:newpack];
    }
    for(int j=0; j<+2; j++){
        ((Pack *)[((Deck *)[globDat.allDecks objectAtIndex:0]).packs objectAtIndex:j]).packName=@"pack name";
    }

    NSLog(@"*** NIL sample pack name=%@",((Pack *)[((Deck *)[globDat.allDecks objectAtIndex:0]).packs objectAtIndex:0]).packName);
 //always returns null

It's pretty cumbersome to work the structure. Is this the best way to manage this data?

Plus, the encoding doesn't seem to save the embedded arrays (Pack and Card).

Upvotes: 0

Views: 144

Answers (2)

FreeAsInBeer
FreeAsInBeer

Reputation: 12979

Honestly I would continue with the way you are doing things. The reason that Pack and Card aren't being saved is because they each need to implement the encodeWithCoder: and initWithCoder: methods.

Card.h

@interface Card : NSObject

@property (nonatomic,retain)NSString *primaryPhrase;
@property (nonatomic,retain)NSString *secondaryPhrase;

@end

Card.m

@implementation Card

@synthesize primaryPhrase, secondaryPhrase;

-(id)init{
    if(self=[super init]){
    }
    return self;
}

-(void)encodeWithCoder:(NSCoder*)encoder{
    [encoder encodeObject:primaryPhrase forKey:@"primaryPhrase"];
    [encoder encodeObject:secondaryPhrase forKey:@"secondaryPhrase"];
}

-(id)initWithCoder:(NSCoder*)decoder{
    if(self=[super init]){
        primaryPhrase=[decoder decodeObjectForKey:@"primaryPhrase"];
        secondaryPhrase=[decoder decodeObjectForKey:@"secondaryPhrase"];
    }
    return self;
}

@end

Pack.h

@interface Pack : NSObject

@property (nonatomic,retain)NSMutableArray  *cards;
@property (nonatomic,retain)NSString        *packName;
@property (nonatomic,assign)BOOL            isInUse;

@end

Pack.m

@implementation Pack

@synthesize packName, cards, isInUse;

-(id)init{
    if(self=[super init]){
        self.isInUse=YES;
    }
    return self;
}

-(void)encodeWithCoder:(NSCoder*)encoder{
    [encoder encodeObject:packName forKey:@"packName"];
    [encoder encodeObject:cards forKey:@"cards"];
    [encoder encodeObject:[NSNumber numberWithBool:isInUse] forKey:@"isInuse"];
}

-(id)initWithCoder:(NSCoder*)decoder{
    if(self=[super init]){
        packName=[decoder decodeObjectForKey:@"packName"];
        cards=[decoder decodeObjectForKey:@"cards"];
        isInUse=[[decoder decodeObjectForKey:@"isInUse"] boolValue];
    }
    return self;
}

@end

Deck.h

@interface Deck : NSObject <NSCoding>

@property (nonatomic,retain)NSMutableArray  *packs;
@property (nonatomic,retain)NSString        *deckName;
@property (nonatomic,retain)NSString        *primaryLang;
@property (nonatomic,retain)NSString        *secondaryLang;

@end

Deck.m

#import "Deck.h"
@implementation Deck

@synthesize packs, deckName, primaryLang, secondaryLang;

-(id)init{
    if(self=[super init]){
    }
    return self;
}

-(void)encodeWithCoder:(NSCoder*)encoder{
    [encoder encodeObject:packs forKey:@"packs"];
    [encoder encodeObject:deckName forKey:@"deckName"];
    [encoder encodeObject:primaryLang forKey:@"primaryLang"];
    [encoder encodeObject:secondaryLang forKey:@"secondaryLang"];
}

-(id)initWithCoder:(NSCoder*)decoder{
    if(self=[super init]){
        packs=[decoder decodeObjectForKey:@"packs"];
        deckName=[decoder decodeObjectForKey:@"deckName"];
        primaryLang=[decoder decodeObjectForKey:@"primaryLang"];
        secondaryLang=[decoder decodeObjectForKey:@"secondaryLang"];
    }
    return self;
}

Upvotes: 1

FluffulousChimp
FluffulousChimp

Reputation: 9185

I'll post this as an answer, though it's really an opinion.

I would use Core Data for the model layer. You will not need to deal with serializing your object graph as your are now. Rather, the object graph persistence is largely handled by the framework. There is a learning curve - per Apple, it is not an "entry-level technology" - but it will be much more manageable in the long run.

As for the issue with serialization of the arrays in your object graph, NSMutableArray conforms to the NSCoding protocol; there is some other issue. Instead of:

packs=[decoder decodeObjectForKey:@"packs"];

don't you mean:

self.packs = [decoder decodeObjectForKey:@"packs"];

or

packs = [[decoder decodeObjectForKey:@"packs"] retain];

(I'm assuming you're not using ARC...)

Upvotes: 1

Related Questions