cs193p – Assignment #6 Extra Task #3

cs193p – assignment #6 extra task #3

Please note, this blog entry is from a previous course. You might want to check out the current one.

If you were to use your application for weeks, your database would start to get huge! Implement a mechanism for pruning your database over time. For example, you might want to delete photos a week after you download them?

Add an additional date attribute created to the Photo entity to store when a photo has been added to the database. (Don’t forget to recreate its sub class).

Set the new attribute when a photo is added:

+ (Photo *)photoWithFlickrInfo:(NSDictionary *)photoDictionary
        inManagedObjectContext:(NSManagedObjectContext *)context
         existingPhotographers:(NSMutableArray *)photographers
               existingRegions:(NSMutableArray *)regions
{
    ...    
    photo.created = [NSDate date];
    ...
}

… because the database schema has changed, it’s necessary to remove the app from the simulator/device … alternatively you could try lightweight migration …

Trigger the pruning of the database from the application delegate as soon as the database is available:

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ...
    [DocumentHelper useDocumentWithOperation:^(UIManagedDocument *document, BOOL success) {
        if (success) {
            ...
            [Photo removeOldPhotosFromManagedObjectContext:document.managedObjectContext];
        }
    }];
    ...
}

Fetch all photos older than one week and remove them:

#define TIMETOREMOVEOLDPHOTS 60*60*24*7
+ (void)removeOldPhotosFromManagedObjectContext:(NSManagedObjectContext *)context
{
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
    request.predicate = [NSPredicate predicateWithFormat:@"created < %@", [NSDate dateWithTimeIntervalSinceNow:-TIMETOREMOVEOLDPHOTS]];
    NSError *error = nil;
    NSArray *matches = [context executeFetchRequest:request error:&error];
    if (!matches || error) {
        // handle error
    } else if (![matches count]) {
        // nothing to do
    } else {
        for (Photo *photo in matches) {
            [photo remove];
        }
        [context save:nil];
    }
}

Before removing the photo, check if there is only this photo assigned to the photographer of this photo. If yes, remove that photographer. Check if the current photo is the only photo for this region. If yes, remove the region, otherwise adjust the counters for photographers and photos. If the current photo is also one of the last 20 viewed photos, take care of that, too. Finally, remove the photo itself:

- (void)remove
{
    if ([self.photographer.photos count] == 1) {
        [self.managedObjectContext deleteObject:self.photographer];
    }
    if ([self.region.photos count] == 1) {
        [self.managedObjectContext deleteObject:self.region];
    } else {
        self.region.photographerCount = @([self.region.photographers count]);
        self.region.photoCount = @([self.region.photos count] - 1);
    }
    if (self.recent) {
        [self.managedObjectContext deleteObject:self.recent];
    }
    [self.managedObjectContext deleteObject:self];
}

The complete code for the extra task #3 is available on github.

FacebooktwitterredditpinterestlinkedintumblrmailFacebooktwitterredditpinterestlinkedintumblrmail

3 thoughts on “cs193p – Assignment #6 Extra Task #3”

  1. Thanks for posting the solution and detail explanation. However, I found this remove function has a few bugs. when remove the photo or photographers from region, it has to use removePhotosObject or removePhotogrphersObject to disconnect the relationship between the entities. it seems by removing the object of photos and photographers doesn’t decrease the count of the relationships. Also, the count of phototgraphers in a region could be different because in the above function only remove the photographer if it has 0 photo, but a photographer could have 0 photos in a region but some photos in another regions. In this case, we also have to decrease the number of photographers in this region.

  2. I created an extra method call
    +(NSInteger) num_photo_of_photographer:(Photographer *)photographer in_region:(Region *)region
    to find out how many photo of the photographer that associated with current photo, and delete the photographer from the region if that is the only photo he has in this region.

    -(void) remove {
    //remove regions
    if ([self.region.photos count] == 1) {
    //NSLog(@”remove region %@”, self.region);
    [self.managedObjectContext deleteObject:self.region];
    } else {
    if (!self.region) {
    NSLog(@”photo region is null %@”,self);
    } else {
    if ([Photo num_photo_of_photographer:self.photographer in_region:self.region] == 1) {
    self.region.num_photographers = @([self.region.num_photographers intValue] -1);
    [self.region removePhotographersObject:self.photographer];
    }
    self.region.num_photos = @([self.region.num_photos intValue]-1);
    [self.region removePhotosObject:self];
    }
    }
    //remove photographer
    if ([self.photographer.photos count] == 1) {
    [self.managedObjectContext deleteObject:self.photographer];
    }
    if (self.recent) {
    [self.managedObjectContext deleteObject:self.recent];
    }
    [self.managedObjectContext deleteObject:self];
    }

    +(NSInteger) num_photo_of_photographer:(Photographer *)photographer in_region:(Region *)region
    {
    NSInteger result = 0;
    for (Photo *photo in region.photos) {
    if ([photo.photographer isEqual:photographer]) {
    result++;
    }
    }
    return result;
    }

Leave a Reply

Your email address will not be published.