My Challenge Solution


#1

I’m offering my solution. The first half is from the Stockz code, the solution starts about halfway through. Plenty of sparse comments.

[code]//
// main.m
// Stockz
//
// Created by Brown David on 12/18/11.
// Copyright © 2011 MyCompanyName. All rights reserved.
//

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

@autoreleasepool {
    
    // insert code here...
    // start of original stockz
    NSMutableArray *stocks = [[NSMutableArray alloc] init];
    
    NSMutableDictionary *stock;
    
    stock = [NSMutableDictionary dictionary];
    
    [stock setObject:@"APPL"
              forKey:@"symbol"];
    [stock setObject:[NSNumber numberWithInt:200]
              forKey:@"shares"];
    [stocks addObject:stock];
    
    stock = [NSMutableDictionary dictionary];
    
    [stock setObject:@"GOOG"
              forKey:@"symbol"];
    [stock setObject:[NSNumber numberWithInt:160]
              forKey:@"shares"];
    [stocks addObject:stock];
    
    [stocks writeToFile:@"/tmp/stocks.plist"
             atomically:YES];
    
    NSLog(@"Done!");
    
    NSArray *stockList = [NSArray arrayWithContentsOfFile:@"/tmp/stocks.plist"];
    
    for (NSDictionary *d in stockList) {
        NSLog(@"I have %@ shares of %@", [d objectForKey:@"shares"], [d objectForKey:@"symbol"]);
    }
    
    // start of challenge code for property list - Not the most efficient way to create objectgs but it does get the task done
    NSMutableArray *plistTypes = [[NSMutableArray alloc] init];
    
    NSMutableDictionary *plistType;
    
    // sparse comments below, given that the forKey pretty much comments on what is being done after the fact
    
    plistType = [NSMutableDictionary dictionary];

    NSDate *now = [[NSDate alloc] init];
    NSDate *tomorrow = [now dateByAddingTimeInterval:24.0 * 60.0 * 60.0];
    NSData *yesterday = [now dateByAddingTimeInterval:-24.0 *60.0 * 60.0];
    
    NSArray *theNSArray = [NSArray arrayWithObjects:yesterday, now, tomorrow, nil];
    
    [plistType setObject:theNSArray forKey:@"theNSArray"];

    [plistTypes addObject:plistType];
    
    plistType = [NSMutableDictionary dictionary];
    
    [stock setObject:@"APPL"
              forKey:@"symbol"];
    [stock setObject:[NSNumber numberWithInt:200]
              forKey:@"shares"];
    
    [plistType setObject:stock
                  forKey:@"theNSDictionary"];
    
    [plistTypes addObject:plistType];
    
    plistType = [NSMutableDictionary dictionary];
    
    [plistType setObject:@"This is a NSString"
                  forKey:@"theNSString"];
    
    [plistTypes addObject:plistType];
    
    plistType = [NSMutableDictionary dictionary];
    
    // copy png of your chonce and use that otherwise this will fail (error handling isn't here)
    NSData *dataPNG = [NSData dataWithContentsOfFile:@"/tmp/1.png"];
    // if dataPNG is NULL, you'll know it here.
    [plistType setObject:dataPNG
                  forKey:@"theNSData"];
    
    [plistTypes addObject:plistType];
    
    plistType = [NSMutableDictionary dictionary];
    
    [plistType setObject:now
                  forKey:@"theNSDate"];
    
    [plistTypes addObject:plistType];
    
    plistType = [NSMutableDictionary dictionary];
    
    [plistType setObject:[NSNumber numberWithInt:100]
                  forKey:@"theInteger"];
    
    [plistTypes addObject:plistType];
    
    plistType = [NSMutableDictionary dictionary];
    
    [plistType setObject:[NSNumber numberWithFloat:100.00005]
                  forKey:@"theFloat"];
    
    [plistTypes addObject:plistType];
    
    plistType = [NSMutableDictionary dictionary];
    
    [plistType setObject:[NSNumber numberWithBool:YES]
                  forKey:@"theBOOL"];
    
    [plistTypes addObject:plistType];
    
    [plistTypes writeToFile:@"/tmp/plistType.plist"
                   atomically:YES];
    
    NSArray *plistTypeList = [NSArray arrayWithContentsOfFile:@"/tmp/plistType.plist"];
    
    // This seems a little kludgey, but it works -- probably a recursion type call would be a better implementation here
    for (NSDictionary *d in plistTypeList) {
        for (id key in d) {
            NSDictionary *innerItem = [d objectForKey:key];
            // comment out if if you want to use just classname to confirm contents
            if ([innerItem isKindOfClass:[NSArray class]]) {
                NSLog(@"theNSArray");
            } else if ([innerItem isKindOfClass:[NSDictionary class]]) {
                NSLog(@"theNSDictionary");
            } else if ([innerItem isKindOfClass:[NSString class]]) {
                NSLog(@"theNSString");
            } else if ([innerItem isKindOfClass:[NSData class]]) {
                NSLog(@"theNSData");
                // remove next line if you want to print contents of theNSData
                continue;
            } else if ([innerItem isKindOfClass:[NSDate class]]) {
                NSLog(@"theNSDate");
            } else if ([innerItem isKindOfClass:[NSNumber class]]) {
                NSLog(@"NSNUMBER");
            }
            // [innerItem className] sort of confirms I've got this but I don't know how to distingush between, integer, float and boolean 
            NSLog(@"Contents: %@ of type %@", d, [innerItem className]);
        }
               }
    
    NSLog(@"Done again!");

}

return 0;

}

[/code]


#2

Good to see I wasn’t the only one that seemed to take the easy approach and do it all in one place…

//
//  main.m
//  Ch26Challenge
//
//  Create a property list with all 8 types in it: array, dictionary, string, data, date, integer
//  float, boolean.
//

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    @autoreleasepool {
        
        // Create a mutable array to contain the entire property list
        NSMutableArray *thePropertyList = [[NSMutableArray alloc] init];
        
        // Create an array with arbitrary data
        NSArray *array = [NSArray arrayWithObjects:@"Banana", @"Apple", @"Lemon", @"Grapefruit", @"Kiwi Fruit", nil];
        [thePropertyList addObject:array];
        
        // Create a dictionary
        NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"4GB", @"Memory", @"13.3 inches", @"Screen size", nil];
        [thePropertyList addObject:dictionary];
        
        // Create a string
        NSString *string = [NSString stringWithFormat:@"This is a string"];
        [thePropertyList addObject:string];
        
        // Create a data object
        NSData *data = [NSData dataWithContentsOfFile:@"/tmp/google.png"];
        [thePropertyList addObject:data];
        
        // Create a date
        NSDate *aDate = [NSDate date];
        [thePropertyList addObject:aDate];
        
        // Create an integer
        int integer = 35;
        [thePropertyList addObject:[NSNumber numberWithInt:integer]];
        
        // Create a float
        float floating = 13.8;
        [thePropertyList addObject:[NSNumber numberWithFloat]];
        
        // Create a boolean
        BOOL boolean = YES;
        [thePropertyList addObject:[NSNumber numberWithBool:boolean]];
        
        [thePropertyList writeToFile:@"/tmp/thePropertyList.plist" atomically:YES];
        
    }
    return 0;
}

#3

My solution :

[code]int main (int argc, const char * argv[])
{

@autoreleasepool {
    
   //Write a tool that creates a property list that has all 8 types in it :
    //array, dictionary, string, data, date, integer, float, boolean
    
    //Create a mutable array that will hold the 8 types of properties
    NSMutableArray *myPList = [[NSMutableArray alloc]init];
    
    //Create the array
    NSArray *animals = [[NSArray alloc]initWithObjects:@"zebra", @"donkey", nil];
    [myPList addObject:animals];
    
    //Create the dictionary
    NSMutableDictionary *trophies = [[NSMutableDictionary alloc]init];
    [trophies setObject:@"99%" forKey:@"Demons' Souls"];
    [trophies setObject:@"45%" forKey:@"Drakes 3"];
    [trophies setObject:@"33%" forKey:@"GTA IV"];
    [trophies setObject:@"0%" forKey:@"Street Fighter IV Demo"];
    [myPList addObject];
    
    //Create the string
    NSString *compellingStatement = @"Hello, I am a String!!!";
    [myPList addObject:compellingStatement];
    
    //Create the third type, data
    NSData *someData = [[NSData alloc]init];
    [myPList addObject];
    
    //Create date
    NSDate *now = [NSDate date];
    [myPList addObject:now];
    
    //Create the integer
    NSNumber *rotations = [[NSNumber alloc]initWithInt:44];
    [myPList addObject:rotations];
    
    //Create the float
    float floating = 13.8;
    NSNumber *myFloat = [[NSNumber alloc]initWithFloat];
    [myPList addObject:myFloat];
    
    //Create the boolean 
    NSNumber *myBool = [[NSNumber alloc]initWithBool:YES];
    [myPList addObject:myBool];
    
    
    //Create the PList file from the myPlist array of objects
    [myPList writeToFile:@"/Users/Bulletcam/Documents/ch26challenge.plist" atomically:YES];
    
}
return 0;

}
[/code]

A few observations :

#1 - When creating a PList with numbers, you can’t just use a primitive - ie - int myNum = 44 - You have to wrap it in an NSNumber object ie - NSNumber *rotations = [[NSNumber alloc]initWithInt:44];

#2 - You don’t seem to need to declare an int before creating a new object, you can just use the initWithSomething method and throw in the value there which saves two lines of code. ie - instead of :
int myFaveNum = 44;
NSNumber *theNumber = [[NSNumber alloc] init];
[theNumber setValue:myFaveNum];

Just say:
NSNumber *theNumber = [[NSNumber alloc] initWithInteger:44];

#3 - When the PList is written to disk and you open it, the float and integer types are both called “Number” in the PList, but the values show an integer or a float. You can edit the Plist and put anyting you want in that field, float, int, whatever, so it seems like it makes no difference in the code, the Plist doesn’t differentiate.

Am I missing something on point #3 here?


#4

You can go even more concise than that by using the class methods:

NSNumber *theNumber = [NSNumber numberWithInt:44];

[quote]
#3 - When the PList is written to disk and you open it, the float and integer types are both called “Number” in the PList, but the values show an integer or a float. You can edit the Plist and put anyting you want in that field, float, int, whatever, so it seems like it makes no difference in the code, the Plist doesn’t differentiate.

Am I missing something on point #3 here?[/quote]

No, I believe you’ve assessed it correctly. The libraries can distinguish between floats and integers easily enough simply by looking for a decimal point, so there’s no need to mark them up differently in the property list file.

I’d have to test to verify, but if you store [NSNumber numberWithFloat:4] in a property list file, I imagine it would be stored as 4.0 so that when parsed it would be re-created as a float value.


#5

Thanks for the tip about using the class method NumberWithFloat, I tried it and it works great!

I tried the following line of code : NSNumber *myFloat = [NSNumber numberWithFloat:4.0];

And checked the PList file I created at the end of the exercise, but it doesn’t show 4.0 in the PList, just 4. Good to know these things I guess. I’m running XCode 4.2 on Mac OSX 10.6.8