Anyone do the gold challenge? (Spoiler alert)


I wasn’t completely sure what the authors had in mind, but here’s what I did.

(1.) Reverse the logic in the readFromJSONDictionary methods to build new createJSONDictionary methods.

(2.) Replace archiving/unarchiving operations with binary read/write operations of JSON dictionaries.

For RSSItem:

- (NSDictionary *)createJSONDictionary
    NSMutableDictionary *json = [[NSMutableDictionary alloc] init];
    // add title iVar to json
    NSDictionary *titleDict = [NSDictionary dictionaryWithObject:[self title] forKey:@"label"];
    [json setObject:titleDict forKey:@"title"];
    // add link iVar to json
    NSDictionary *hrefDict = [NSDictionary dictionaryWithObject:[self link] forKey:@"href"];
    NSDictionary *attributesDict = [NSDictionary dictionaryWithObject:hrefDict forKey:@"attributes"];
    NSArray *linkArray = [NSArray arrayWithObjects:@"empty", attributesDict, nil];   // see Note #1 below
    [json setObject:linkArray forKey:@"link"];
    return json;

For RSSChannel:

- (NSDictionary *)createJSONDictionary
    NSMutableDictionary *json = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *feedDict = [[NSMutableDictionary alloc] init];
    // add items to json
    NSMutableArray *arrayOfEntries = [[NSMutableArray alloc] init];
    NSDictionary *entry;
    for (RSSItem *i in items) {
        entry = [i createJSONDictionary];
        [arrayOfEntries addObject:entry];
    [feedDict setObject:arrayOfEntries forKey:@"entry"];
    // add title to json
    [feedDict setObject:[self title] forKey:@"title"];
    [json setObject:feedDict forKey:@"feed"];
    return json;

From the data store’s fetchTopSongs:withCompletion: method:

// to load JSON
RSSChannel *cachedChannel = [[RSSChannel alloc] init];
[cachedChannel readFromJSONDictionary:[[NSDictionary alloc] initWithContentsOfFile:cachePath]]; 

// to cache JSON
[[obj createJSONDictionary] writeToFile:cachePath atomically:YES];  // binary write operation

Note #1: you need a filler object here because the good stuff is kept at index 1


Hi, I write a similar solution for the challenge:

I have created a new method for the protocol


Then implement it in the classes that conform to this protocol
In RSSChannel.m

[code]-(NSDictionary *)writeToJSONDictionary
//we create a dictionary with the same structure that the one we get from the web service
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:
[[NSMutableDictionary alloc] initWithObjectsAndKeys:
[[NSDictionary alloc] initWithObjectsAndKeys:
[self title],

NSMutableArray *arrayItems = [[NSMutableArray alloc] init];
for(RSSItem *it in items){
    NSDictionary *itemDict = [it writeToJSONDictionary];
    [arrayItems addObject];

//save the entry array in the dictionary
[[dict objectForKey:@"feed"] setObject:arrayItems forKey:@"entry"];
return dict;


and in RSSItem.m

[code]-(NSDictionary *)writeToJSONDictionary

//we create a dictionary with the same structure that the one we get from the web service
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:
                      [[NSDictionary alloc] initWithObjectsAndKeys:
                       [self title],
                      [NSArray arrayWithObject:[[NSDictionary alloc] initWithObjectsAndKeys:
                                                [[NSDictionary alloc] initWithObjectsAndKeys:
                                                 [self link],

return dict;


Finally just change the form of retrieving data from and saving to the cache, just like zzzzzzzzzzz
So in -(void)fetchTopSongs:(int)count withCompletion:(void (^)(RSSChannel *obj, NSError *err))block from BNRFeedStore.m I have

[code]-(void)fetchTopSongs:(int)count withCompletion:(void (^)(RSSChannel *obj, NSError *err))block

//NSLog(@"Stored date: %@", [self topSongsCacheDate]);

//make the path for saving cache
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
cachePath = [cachePath stringByAppendingPathComponent:@"apple.archive"];

//checking for the date of the cache, if exist
NSDate *tscDate = [self topSongsCacheDate];
    //how old is the cache?
    NSTimeInterval cacheAge = [tscDate timeIntervalSinceNow];
    if(cacheAge > -300.0){//-300
        //if it is older than 5 minutes, here we go
        //NSLog(@"Reading caché!");
        //we try to get the data from the cache file
        NSData *data = [NSData dataWithContentsOfFile:cachePath];
        RSSChannel *cachedChannel;
        //if the file is not created yet, data = nil, so don't do anything with the cache
            NSDictionary *cachedDictionary = [NSJSONSerialization JSONObjectWithData:data
            cachedChannel = [[RSSChannel alloc] init];
            [cachedChannel readFromJSONDictionary:cachedDictionary];
            cachedChannel = nil;
            //controller block, that way the block is invoked after fetchEntries finish its task
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                block(cachedChannel, nil);
            //there is nothing to do, because there is no cache

//prepare the url
NSString *requestString = [NSString stringWithFormat:@"", count];

NSURL *url = [NSURL URLWithString:requestString];

//setting the connexion
NSURLRequest *request = [NSURLRequest requestWithURL:url];

RSSChannel *channel = [[RSSChannel alloc] init];

XLConnection *connection = [[XLConnection alloc] initWithRequest:request];

[connection setCompletionBlock:^(RSSChannel *obj, NSError *err){
    //block of the store
    //if everything goes fine, save the data into the caché in json format, and topSongsCacheDate in User preferences
    if(! err){
        [self setTopSongsCacheDate:[NSDate date]];
        NSDictionary *dict = [obj writeToJSONDictionary];
        //just for debugging, the format is OK
        //NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:nil];
        //NSLog(@"%@", [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]);
        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:kNilOptions error:nil];
        //saving the file in disk
        [jsonData writeToFile:cachePath atomically:YES];
    //ejecutamos el bloque que viene del controlador
    block(obj, err);

[connection setJsonRootObject:channel];

[connection start];


I think that is what the author proposed in the challenge, and that is working fine, but any advice would be apreciated.


@ zzzzzzzzzzz & xiledo

Thanks guy, this is just brilliant !
Your posts helped me pass a few hurdles and better understand how to structure the JSON format.



Yeap. See my post. Somewhat similar to what others did. Perhaps my only contribution is the use of isValidJSONObject. :slight_smile: