Challenge: So Many Questions, So Little Time


#1

I did get this to work, but I’m not at all confident in it:

PropertyListObject.h

[code]#import <Foundation/Foundation.h>

@interface PropertyListObject : NSObject
{
NSMutableDictionary *information;
NSData *picture;
}

@property (strong) NSData *picture;
@property (strong) NSMutableDictionary *information;

-(void)addInformationToDictionary:(NSMutableDictionary *)dictionaryEntry;
-(NSData *)retrieveImage:(NSString *)url;

@end[/code]

PropertyListObject.m

[code]#import “PropertyListObject.h”

@implementation PropertyListObject

@synthesize picture;
@synthesize information;

-(void)addInformationToDictionary:(NSMutableDictionary *)dictionaryEntry
{
if(!information) {
information = [NSMutableDictionary dictionary];
}
[information addEntriesFromDictionary:dictionaryEntry];
}

-(NSData *)retrieveImage:(NSString *)url
{
NSURL *urlToGet = [NSURL URLWithString:url];
NSURLRequest *request = [NSURLRequest requestWithURL];
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:&error];

if(!data) {
    NSLog(@"fetch failed: %@", [error localizedDescription]);
} 
return data;

}
@end[/code]

main.m

[code]
. . . .
@autoreleasepool {

    //Create an instance of the PropertyListObject.
    PropertyListObject *aPerson = [[PropertyListObject alloc] init];
    
    //Create a local copy of a dictionary.
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
    [dictionary setObject:@"BillyJoe Jimbob" forKey:@"name"];
    [aPerson addInformationToDictionary:dictionary];
    
    NSMutableDictionary *dictionary2 = [NSMutableDictionary dictionary];
    [dictionary2 setObject:[NSNumber numberWithFloat:5.833] forKey:@"height"];
    [aPerson addInformationToDictionary:dictionary2];

    NSMutableDictionary *dictionary3 = [NSMutableDictionary dictionary];
    [dictionary3 setObject:[NSNumber numberWithInt:46] forKey:@"age"];
    [aPerson addInformationToDictionary:dictionary3];

    NSMutableDictionary *dictionary4 = [NSMutableDictionary dictionary];
    [dictionary4 setObject:@"M" forKey:@"sex"];
    [aPerson addInformationToDictionary:dictionary4];
    
    NSMutableDictionary *dictionary5 = [NSMutableDictionary dictionary];
    [dictionary5 setObject:[NSDate dateWithString:@"1965-07-01 07:47:52 -400"] forKey:@"birthday"];
    [aPerson addInformationToDictionary:dictionary5];
    
    NSMutableDictionary *dictionary6 = [NSMutableDictionary dictionary];
    [dictionary6 setObject:@"YES" forKey:@"motorcycleRider"];
    [aPerson addInformationToDictionary:dictionary6];
    
    NSDictionary *fullInformation = [aPerson information];
    [fullInformation writeToFile:@"/Users/jbradleyhorn/Documents/PListTest.plist" atomically:YES];
    
    // obtain the picture URL, get the data, then set the variable.
    NSString *url = @"http://farm7.staticflickr.com/6073/6040506206_20b4d197c1_b.jpg";
   // The one on the left is my KTM!!!
    NSData *pix = [aPerson retrieveImage:url];
    [aPerson setPicture:pix];

    //Write it all to a file.
    NSMutableArray *pListTest = [[NSMutableArray alloc] init];
    [pListTest addObject:pix];
    [pListTest addObject:fullInformation];
    [pListTest writeToFile:@"/Users/jbradleyhorn/Documents/PListTest.plist"
              atomically:YES];
}
return 0;

}[/code]

Unlike the example on page 173, I could not reuse the dictionary. I had to give my dictionary entires a different variable name in Main to make it work. I even tried to “nil” out the old dictionary before reuse, but it wouldn’t take it – subsequent entries would just not go into the object’s dictionary.

Also, I could retrieve the picture and record that in the object’s NSData variable, but I could not do something like this and make it write to an array:

[pListTest addObject:PropertyListObject];

If I did that, all of the dictionary entries would make it into the plist, but the NSData stuff would be missing. If I add the two elements from the PropertyListObject as separate Array entries, I could get the picture’s data into the plist as a separate entry. I don’t know why.

I’m not sure that the methods I wrote are appropriate. Would it be more appropriate to write the “retrieveImage” method as “retrieveAndSaveImage”? Maybe that would be better for object encapsulation? Dunno.

I couldn’t figure out how to get information into an object’s dictionary without writing a method. Is there a better way? Is it possible to get a long string of dictionary objects and associated keys all huddled together as one item, like that labeled “dictionary” above, and then put all of those into the object’s dictionary at one time? It seems more economical than adding dictionary entries to the object over and over and over.

Can you put a float value in an object container, say “int age = 46;” and get that to write to a plist? Could you take an object, like my “aPerson” above, that includes an integer variable and force that into an Array that is then written to a plist?

Not feeling very confident in all of this, obviously.


#2

I’m not able to reproduce your problem, so I’m not sure what to advise. I’ve taken your code and run it as is, with one dictionary used over and over, and with new dictionaries created each time but assigned to the same variable, and in all 3 cases the plist file shows the same set of data:

        <dict>
                <key>age</key>
                <integer>46</integer>
                <key>birthday</key>
                <date>1965-07-01T11:47:52Z</date>
                <key>height</key>
                <real>5.8330001831054688</real>
                <key>motorcycleRider</key>
                <string>YES</string>
                <key>name</key>
                <string>BillyJoe Jimbob</string>
                <key>sex</key>
                <string>M</string>
        </dict>

Note that there’s no reason to use different dictionaries at all; you can create as many key/value pairs as you wish in just one dictionary.

If you can create the smallest example code that doesn’t (but should) work, please add it to the thread and I’ll see if I can sort out what’s wrong.

If you look at the description of writeToFile:atomically: in Apple’s NSArray reference, you’ll see that the nodes (including nested objects inside other collections) of any array/dictionary written to disk must be only property list objects (defined by Apple as the basic types: NSString, NSData, NSArray, NSDictionary) in order to write to file. Your class is not one of those, despite only containing those values, so it can’t be written.

It’s always a little tough in small applications like this (particularly a small bogus application with no real-world problem to solve) to come up with a coherent encapsulation and naming strategy, because there isn’t enough code to organize it particularly well without creating multiple tiny classes. You can’t save the image to disk by itself (or else it’ll get overwritten with the next save) so “retrieveAndSave” is probably a bad name.

I wouldn’t worry too much about the organization on this one.

If you used the same dictionary to add all key/value pairs, you could pass it to setInformation in your class, and that’s all that you’d need to do.

If you really want a copy of the dictionary, you should be able to do what you did, but only call addInformationToDictionary once, at the very end.

You can definitely store integers and real values in NSNumber to be stored in a collection class like an array or dictionary. Despite NSNumber not being on the list of property list classes I listed above, clearly that does work.


#3

You were right. I’m getting it. Here it is tightened up. Thanks very much.

PropertyListObject.h

[code]#import <Foundation/Foundation.h>

@interface PropertyListObject : NSObject
{
NSMutableDictionary *information;
NSData *picture;
}

@property (strong) NSData *picture;
@property (strong) NSMutableDictionary *information;

-(NSData *)retrieveImage:(NSString *)url;

@end[/code]

PropertyListObject.m

[code]#import “PropertyListObject.h”

@implementation PropertyListObject

@synthesize picture;
@synthesize information;

-(NSData *)retrieveImage:(NSString *)url
{
NSURL *urlToGet = [NSURL URLWithString:url];
NSURLRequest *request = [NSURLRequest requestWithURL];
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:NULL error:&error];

if(!data) {
    NSLog(@"fetch failed: %@", [error localizedDescription]);
} 
return data;

}
@end[/code]

main.m

[code]#import <Foundation/Foundation.h>
#import “PropertyListObject.h”

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

    //Create an instance of the PropertyListObject.
    PropertyListObject *aPerson = [[PropertyListObject alloc] init];
    
    //Create a local copy of a dictionary.
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
    //Load it up.
    [dictionary setObject:@"BillyJoe Jimbob" forKey:@"name"];
    [dictionary setObject:[NSNumber numberWithFloat:5.833] forKey:@"height"];
    [dictionary setObject:[NSNumber numberWithInt:46] forKey:@"age"];
    [dictionary setObject:@"M" forKey:@"sex"];
    [dictionary setObject:[NSDate dateWithString:@"1965-07-01 07:47:52 -400"] forKey:@"birthday"];
    [dictionary setObject:@"YES" forKey:@"motorcycleRider"];
    [aPerson setInformation:dictionary];
   
    // obtain the picture URL, get the data, then set the variable.
    NSString *url = @"http://farm7.staticflickr.com/6073/6040506206_20b4d197c1_b.jpg";
    NSData *pix = [aPerson retrieveImage:url];
    [aPerson setPicture:pix];

    //Write it all to a file.
    NSMutableArray *pListTest = [[NSMutableArray alloc] init];
    [pListTest addObject:[aPerson picture]];
    [pListTest addObject:[aPerson information]];
    [pListTest writeToFile:@"/Users/jbradleyhorn/Documents/PListTest.plist"
              atomically:YES];
}
return 0;

}[/code]

The “picture” will write to the plist when added as a separate object in the array. Thanks again.


#4

You’re welcome, glad I could help. The new code looks much cleaner.