Setting up a plist to store application data (not settings) for an iPhone game
Asked Answered
C

2

4

I am writing an iPhone game and have it working well with my level data hard coded but I would like to store the level data in a plist a load it at launch. I have never used plists and am having trouble understanding how I should set the plist up based on my data model.

Here is how I have it hard coded now:

(in my appDelegate)

- (void)loadLevels {
    //setup NSNumber objects to load into sequences
    NSNumber * rID = [[[NSNumber alloc] initWithInt:0] autorelease];
    NSNumber * bID = [[[NSNumber alloc] initWithInt:1] autorelease];
    NSNumber * gID = [[[NSNumber alloc] initWithInt:2] autorelease];
    NSNumber * yID = [[[NSNumber alloc] initWithInt:3] autorelease];
    NSNumber * rbID = [[[NSNumber alloc] initWithInt:4] autorelease];
    NSNumber * rgID = [[[NSNumber alloc] initWithInt:5] autorelease];
    NSNumber * ryID = [[[NSNumber alloc] initWithInt:6] autorelease];
    NSNumber * bgID = [[[NSNumber alloc] initWithInt:7] autorelease];
    NSNumber * byID = [[[NSNumber alloc] initWithInt:8] autorelease];
    NSNumber * gyID = [[[NSNumber alloc] initWithInt:9] autorelease];

    //Level One's Sequence
    NSMutableArray * aSequence = [[[NSMutableArray alloc] initWithCapacity:1] autorelease];

    [aSequence addObject: rID];
    [aSequence addObject: bID];
    [aSequence addObject: gID];
    [aSequence addObject: yID];
    [aSequence addObject: rbID];
    [aSequence addObject: rgID];
    [aSequence addObject: ryID];
    [aSequence addObject: bgID];
    [aSequence addObject: byID];
    [aSequence addObject: gyID];

    // Load level One
    _levels = [[[NSMutableArray alloc] init] autorelease];

    Level *level1 = [[[Level alloc] initWithLevelNum:1 levelSpeed:1.0 levelSequence:aSequence] autorelease];

    [_levels addObject:level1];
 //do the same thing for subsequent levels//
}

(this is how I have my Level Class implemented)

#import "Level.h"

@implementation Level

@synthesize levelNum = _levelNum;
@synthesize levelSpeed = _levelSpeed;
@synthesize levelSequence = _levelSequence;

- (id)initWithLevelNum:(int)levelNum levelSpeed:(float)levelSpeed levelSequence:(NSMutableArray *)levelSequence {
    if ((self = [super init])) {
        self.levelNum = levelNum;
        self.levelSpeed = levelSpeed;
        self.levelSequence = [[[NSMutableArray alloc] initWithArray:levelSequence] autorelease];
    }
    return self;
}

- (void)dealloc {
    [_levelSequence release];
    _levelSequence = nil;
    [super dealloc];
}

@end

I'm just not getting how to set up a plist to store my data to match my model. Can anyone give me some advise please?

ADDITION: (here is how I think I need to setup the plist - the data model is simply the three variables initializing my level (above). If you look at my current plist it might be more clear how it is setup, but each level is comprised of: a level number, a level speed, and an array of numbers denoting the required sequence for that level.)

My current plist setup

Now, if I do have this setup correctly how do I load the values into my program?

Crossfade answered 6/2, 2011 at 4:59 Comment(2)
Well, what's your model? You don't show us that here.Botts
@Jonathan Grynspan I have updated my question with more detail on my data. Hope this helps to understand what I am trying to do.Crossfade
G
11
  • Add your plist file as a resource file in your project. Say, it's name is config.plist

  • Get the path for the resource file. (Though, be careful of [NSBundle mainBundle] as it will not return the unit test bundle in a unit test.)

NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"config" ofType:@"plist"];
  • Your root object is an array of levels. Load it in a NSArray.
// this is autoreleased. you can retain it if needed
NSArray *levelArray = [NSArray arrayWithContentsOfFile:plistPath];
  • Now you have the array of levels. Each level is a dictionary. Load the level i (counting from 0).
NSDictionary *level = [levelArray objectAtIndex:i];
  • Now you can get the objects from level dictionary by using objectForKey method. For example, to get the sequence array:
NSArray *seq = [level objectForKey:@"levelSequence"];
  • You can loop through the levelArray to alloc, init and add to levels array for all your levels.

Hope it helps. Please note that I have not compiled the code, so there might be some typos.

Garris answered 6/2, 2011 at 5:57 Comment(2)
thank you for the clear and concise answer. As I noted under @Alexsander's answer, I don't need to to update the data at all in this plist it is static, so this approach seems best for my needs! Now one question about KVC: since my plist is and array of dictionaries, does that mean it is not KVC compliant? Or does the fact that the dictionary portion has strings as the first key make it KVC compliant? (This is not really a requirement for my application, but I thought it would be insightful to include in this discussion.)Crossfade
@Mark, I think for static configuration data using plist is better than NSUserDefaults. Like you, I will also prefer NSUserDefaults for data that can be changed like user preference or best score in a game. But for static configuration I will prefer plist. Just like the way we use XML configuration file for other platforms. plist is also XML, with specific elements. About KVC, I am sorry that I am not much experienced on that. So I should not comment on KVC. Let's see what other members suggest.Garris
M
1

What you should do is create an NSDictionary with all of the applicable data that you want, and read it from and write it to NSUserDefaults.

Mimesis answered 6/2, 2011 at 5:27 Comment(6)
@Alexsander Akers I had looked at NSUserDefaults but from what I was reading I thought that was more appropriately used for saving application settings instead of application data. Am I wrong?Crossfade
@Mark7777G: That is its intended purpose but you can store anything you can store in a plist in it. For static data like you want to store I would say storing it in a plist inside the application's bundle (as suggested earlier) is a much more conventional approach.Drub
Further to my last comment, NSUserDefaults is periodically synchronized to disk - with large amounts of data it could create unnecessary overhead.Drub
@Alexsander Akers OK that makes sense, I am looking at using the NSUserDefaults to store application settings & preferences - so that will probably not be too much overhead as you mentioned. I'm thinking taskinoor's solution best fits my needs for the application data portion especially since I won't need to update this info at all - it is static.Crossfade
So I guess the summarization readers can take from this is for small amounts of data (especially data you don't need synchronized often) the plist inside the application bundle is the best approach; however, the NSUserDefaults approach would be best for settings/preferences and data that needs to be synchronized often. Could another optimal use of the plist/application bundle solution be for saved game data that only needs updating at application termination/resign active? Furthermore, am I correct in saying that if you have large amounts of data SQLlite is the best storage solution?Crossfade
@Alexsander Akers , maybe these other questions are better suited for a separate question. I have started a survey question here: stackoverflow.com/questions/4914447/… . Please feel free to comment there as well if you feel it is more appropriate. Thanks again for your contributions to this topic!Crossfade

© 2022 - 2024 — McMap. All rights reserved.