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

12,154

Solution 1

  • 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.

Solution 2

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.

Share:
12,154
Mark7777G
Author by

Mark7777G

See my web site for details about me! http://marksspace.webs.com/

Updated on June 27, 2022

Comments

  • Mark7777G
    Mark7777G almost 2 years

    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?

  • Mark7777G
    Mark7777G about 13 years
    @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?
  • arrtchiu
    arrtchiu about 13 years
    @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.
  • arrtchiu
    arrtchiu about 13 years
    Further to my last comment, NSUserDefaults is periodically synchronized to disk - with large amounts of data it could create unnecessary overhead.
  • Mark7777G
    Mark7777G about 13 years
    @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.
  • Mark7777G
    Mark7777G about 13 years
    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?
  • Mark7777G
    Mark7777G about 13 years
    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.)
  • Mark7777G
    Mark7777G about 13 years
    @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!
  • taskinoor
    taskinoor about 13 years
    @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.