iOS Programming Recipe 13: Using Property Lists (Plists)

In Recipe 11 we created a UITableview which relied on 4 arrays to supply the data sources with data. Since we didn’t have much data, this wasn’t an issue, but what if we wanted 40 different animals? Then our arrays would be very long. One way we can organize this data a little better is through the use of a Property Lists (Plist). Plist can be handy for storing Arrays, Dictionaries, and Strings.

Assumptions

  • You have looked over Part 1 and 2 of the UITableview recipes. I’ll be modifing the Tableview example a bit to remove these arrays and include them into a plist so you may want to go read those.

First, A Word on Property Lists

Plists are basically just text files intended to be more of a readable source of static values. In general, Don’t use them if:

  1. Your data is large. The entire Plist gets loaded into memory and is therefore large data doesn’t play well with them.
  2. You need relationships. Actions such as joins and searches don’t work at all in plist.
  3. You need to update often, edit data, remove items. These are all operations that are better suited for a Core Data or SQL.
  4. Your data does not consist primarily of strings and numbers.

So there you have it in a nutshell! Feel free to check out the documentation on the Mac Developer Library here

Creating The Property List

Alright, while there are a few formats of Plist (XML,binary,ASCII), XML is the most prevailent so we’ll be proceeding with this recipe using XML. Where we left off in the UITableview tutorial, the viewDidLoad method had four defined arrays. We’ll be removing these arrays and putting that data into a Plist instead. The viewDidLoad method looked like this when we left off:


- (void)viewDidLoad
{
    self.bugs = @[@"Spider",@"Ladybug",@"Firefly"];
    self.animals = @[@"Cat",@"Dog",@"Bigfoot"];
    self.scientificBugs = @[@"Araneae",@"Coccinellidae",@"Lampyridae"];
    self.scientificAnimals = @[@"Felis catus",@"Canis lupus familiaris",@"Homo sapiens hirsutii"];
    [super viewDidLoad];
}

From the bottom of the left hand plane push the little plus sign and choose new file from the popup.

How to add files

In the dialogue that comes up choose “Resource” under the “iOS” section and select “Property List” from the window and choose next.

Adding Plist to Project

Now change the name of the list to “creatureList” and press create on the next screen.

Now you’ll be shown a screen that looks like the following:

Default Plist view

While you can edit the property list directly from this screen, it is a bit cumbersome and there is an easier more readable option. Right click the property list in your project navigator and choose Open As -> Source Code. This will bring up a nice text format in a the familiar XML format we all know and love.

Source Code View of XML

Ok! now we’re ready to do a little editing! Edit this XML file as follows. You’ll see there is a relationship between dictionary items and keys. Notice we have added some arrays. This will be helpful for selecting an index path in the tableview.


<!--?xml version="1.0" encoding="UTF-8"?-->

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Bugs</key>
    <array>
        <dict>
            <key>Name</key>
            <string>Spider</string>
            <key>Scientific Name</key>
            <string>Araneae</string>
        </dict>
        <dict>
            <key>Name</key>
            <string>Ladybug</string>
            <key>Scientific Name</key>
            <string>Coccinellidae</string>
        </dict>
        <dict>
            <key>Name</key>
            <string>Firefly</string>
            <key>Scientific Name</key>
            <string>Lampyridae</string>
        </dict>
    </array>
    <key>Animals</key>
    <array>
        <dict>
            <key>Scientific Name</key>
            <string>Felis catus</string>
            <key>Name</key>
            <string>Cat</string>
        </dict>
        <dict>
            <key>Scientific Name</key>
            <string>Canis lupus familiaris</string>
            <key>Name</key>
            <string>Dog</string>
        </dict>
        <dict>
            <key>Scientific Name</key>
            <string>Homo sapiens hisutii</string>
            <key>Name</key>
            <string>Bigfoot</string>
        </dict>
    </array>
</dict>
</plist>

Now we are ready to modify the view controller.

Accessing the Property Lists

In our ViewController.h file I removed the existing array properties and added a couple more. For the remainder of this code I have commented out where the old code used to be for instructional purposes:


@interface ViewController : UIViewController    <UITableViewDataSource, UITableViewDelegate>

/*--
@property (strong, nonatomic) NSArray *bugs;
@property (strong, nonatomic) NSArray *animals;
@property (strong, nonatomic) NSArray *scientificBugs;
@property (strong, nonatomic) NSArray *scientificAnimals;
 */


@property (strong, nonatomic) NSArray *animalArray;
@property (strong, nonatomic) NSArray *bugArray;

@end

Now inside of the viewDidLoad method we’ll add these few lines:


//Load Dictionary with wood name cross refference values for image name
NSString *plistCatPath = [[NSBundle mainBundle] pathForResource:@"creatureList" ofType:@"plist"];
NSDictionary *creatureDictionary = [[NSDictionary alloc] initWithContentsOfFile:plistCatPath];

self.animalArray = creatureDictionary[@"Animals"];
self.bugArray   = creatureDictionary[@"Bugs"];

First we’re creating a string to hold the path for the plist, then we create a dictionary and initialize it with the plist. The next two lines just create arrays from the dictionary for both bugs and animals.

First we had to modify numberOfRowsInSection to reflect our new arrays Like so:


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{    
    switch (section)
    {
        case bugsSection:
            //return [self.bugs count];
            return [self.bugArray count];
        case animalsSection:
            //return [self.animals count];
            return [self.animalArray count];
        default:
            return 0;
    }
}

Then I updated the cellForRowAtIndexPath method like so:


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"creatures"];
   
    UIImage *creatureImage  = nil;
   
    switch (indexPath.section)
    {
        case bugsSection:
            //cell.textLabel.text = [self.bugs objectAtIndex:indexPath.row];
            cell.textLabel.text = self.bugArray[indexPath.row][@"Name"];
            //cell.detailTextLabel.text = [self.scientificBugs objectAtIndex:indexPath.row];
            cell.detailTextLabel.text = self.bugArray[indexPath.row][@"Scientific Name"];
            //creatureImage = [UIImage imageNamed:[NSString stringWithFormat:@"%@%@",[self.bugs objectAtIndex:indexPath.row],@".png"]];
            creatureImage = [UIImage imageNamed:self.bugArray[indexPath.row][@"Name"]];
            break;
        case animalsSection:
            //cell.textLabel.text = [self.animals objectAtIndex:indexPath.row];
            cell.textLabel.text = self.animalArray[indexPath.row][@"Name"];
            //cell.detailTextLabel.text = [self.scientificAnimals objectAtIndex:indexPath.row];
            cell.detailTextLabel.text = self.animalArray[indexPath.row][@"Scientific Name"];
            //creatureImage = [UIImage imageNamed:[NSString stringWithFormat:@"%@%@",[self.animals objectAtIndex:indexPath.row],@".png"]];
            creatureImage = [UIImage imageNamed:self.animalArray[indexPath.row][@"Name"]];
            break;
        default:
            cell.textLabel.text = @"Not Found";
            break;
    }

    cell.imageView.image = creatureImage;
   
    return cell;
}

You’ll notice here that I am first calling the item of each array and then putting [@”Name”] or [@”Scientific Name”] next to that return value. This is using a literal that we spoke of in the last recipe. This is necessary because each item of the array is actually a dictionary object. Basically all it’s doing is saying: ‘”whichever object you return from the array, I want the value for the key “Name” or “Scientific Name”‘ in that dictionary object.

Alright, Now we’ll just need to update the delegate a bit:


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UIAlertView *viewCreatureInfo   = nil;
    NSString    *creatureInfo       = nil;
   
    switch (indexPath.section)
    {
        case bugsSection:
            //creatureInfo = [[NSString alloc] initWithFormat:@"Name:%@ Scientific Name: %@", [self.bugs objectAtIndex:indexPath.row],[self.scientificBugs objectAtIndex:indexPath.row] ];
            creatureInfo = [[NSString alloc] initWithFormat:@"Name:%@ Scientific Name: %@", self.bugArray[indexPath.row][@"Name"], self.bugArray[indexPath.row][@"Scientific Name"]];
            break;
        case animalsSection:
            //creatureInfo = [[NSString alloc] initWithFormat:@"Name:%@ Scientific Name: %@", [self.animals objectAtIndex:indexPath.row],[self.scientificAnimals objectAtIndex:indexPath.row] ];
           
            creatureInfo = [[NSString alloc] initWithFormat:@"Name:%@ Scientific Name: %@", self.animalArray[indexPath.row][@"Name"],self.animalArray[indexPath.row][@"Scientific Name"]];
            break;
        default: creatureInfo = [[NSString alloc] initWithFormat:@"No Creature Found"];
            break;
    }
    viewCreatureInfo = [[UIAlertView alloc] initWithTitle: @"Creature" message:creatureInfo delegate: nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
   
    [viewCreatureInfo show];
}

Nothing special here, more of what we did in the data source methods.

That should pretty much cover it! Now your App should look exactly the same as it did in part two of the Tableview recipe!

Final Product

I do realize that the code here was actually a bit more complicated than just using the four arrays. I could have cleaned it up a tad but I felt it was more instructive to leave it similar to the past tutorial. The benefit here is we can update our plist and add images at will without having to touch our code again.

That’s It for this tutorial! In the future we’ll be talking about other ways to handle data in apps. Using a plist can be handy for small things, but they are not at all recommended for most applications.

About Joe Hoffman

I am An Electrical Engineer that spends a lot of my off time doing web development and other programming. Currently I am trying to expand my knowledge with iOS and I find that writing tutorials helps me learn more thoroughly as well as provide some useful info to others.

Comments

  1. Hi again Joe 🙂
    The XML style plist you posted does not show up correctly her in the web browser, it removes all thingys etc… Hope you saw my other post about populating “DetailView” with a plist 🙂

  2. Awesome man 🙂
    Now the final step.. How to populate a DetailedView with data retrieved from a .plist 🙂
    It would be very very much appreciated.. 😉

    • So by detailed view, do you mean a new view controller that is segued from an element in the table that shows more info?

      • Exactly.. For example more images or additional information about a ladybug..
        Im already working on it but cant get a grip around it.. Surely I must use prepareForSegue method but I dont know how to implement this.. Also all the information on the detailed view must be retrieved from an online plist.. This would be very very useful.. By the way one more thing.. THANKS FOR ALL YOUR HELP! 🙂

        • No Problem Alex,

          I think I may be able to whip something up for you. I’ll try to get something posted on monday or tuesday. I’ll also incorporate the UINavigationController into the tableview.

          As far as web requests go, most services have kind of standardized on JSON or XML for providing data so i’ll probably focus on that. In the mean time, go read the last two recipes (14 and 15): The Weather App, and the UICollectionView Controller tutorial. From those two, you may be able to piece together how to do this on your own.

          -Joe

          • Looking forward to this :’) Ill go straight away and check those two tutorials out… Thanks

  3. Steffen says:

    Is there anyway you could show us with the code how to add a custom detail View for each row that hold data with the plist group? im really struggeling to implement such code with the plist data . i have no way of know how to send it to a Detail ui view through story board

    • Joe Hoffman says:

      Hi Steffen!

      So what you want to do here is create a segue from your prototype cell to a new view controller. Since we didn’t set up a navigation controller for this tutorial this will have to be a modal segue. I would suggest using a navigation controller rather than a view with a UITableView in it for your needs, then you wouldn’t have to create a back button. We used a navigation controller with a UITableViewController in recipe 16. Recipe 16 is very similar to what your trying to do here except you’ll be getting your data from a plist instead of the web. Also take a look at Recipe 5 which deals with the prepareForSegue method. This method is helpful for passing data between two view controllers when you change views.

      Now you have two choices. You can either pass ALL of the data using the prepareForSegue method, or you can just pass the index of the cell and use it to look up your values within the new view controller. This of course would require you to re-define your plist dictionary inside the new view controller.

      Chew on this a little bit and see if you can figure it out. Feel free to ask questions.

  4. At the very beginning you mentioned reasons not to use a plist, “Your data does not consist primarily of strings and numbers.”

    If the information we want to represent is like a catalog full of A LOT of text and some images what would be the best method? This would be a static catalog that would not be changing and cannot be loaded from a website or server unfortunately. Imagine it like a list of speakers and topics at a convention.

    • Similar to the WWDC app right now, the table view displays all the sessions and goes to another view to show the detailed information.

      • Joe Hoffman says:

        HI Chris,

        Sorry I took so long to get back to you. Somehow I missed the notice in my inbox.

        For your situation I would make the calls from a web server and store everything there. If there is some data you would like to keep locally in the case where there is not an internet connection or the data doesn’t change often and you would like to limit pinging the server, I would store that information locally using core data.

        Hope this helps!
        -Joe

  5. Mohamed Reda says:

    if i need to go from each cell to a deferent viewController each viewController have a deferent image array; what the suitable way

    • Joe Hoffman says:

      I’m not entirely sure what relevance to this post your question has, but I would think you would want to use collection views. Each image could take you to a new view controller.

  6. abhishek says:

    one advice for you… please end of topic you add zip file or project download link.. !!

  7. I tried to run your App for PLIST Tutorial. The problem on this App, at when i make any update on the current information and then save the new information. After that when i close the App and open it again , I don’t see my new information and also my plist is not updated. Please,
    Can do help me to fix this part ?Please

    Regards
    A.J.

Trackbacks

  1. […] See more here: iOS Programming Recipe 13: Using Property Lists (Plists) […]

Speak Your Mind

*

css.php
Privacy Policy