Please note, this blog entry is from a previous course. You might want to check out the current one.
Assignment #6 Task #5
All of your table views everywhere in your application (including the Recents tab) must be driven by Core Data (i.e. not NSUserDefaults nor Flickr dictionaries). You no longer have to support “pulling down to refresh” (though see Extra Credit 1).
To handle the recent entity of the database schema, create another category, containing a single class method:
+ (Recent *)recentPhoto:(Photo *)photo;
When this method gets called, check if the photo has already an entry in the recent entity. If yes, just update the last-viewed time stamp. Otherwise connect the current photo in addition to setting the time stamp. For the last assignment, we had to limit number of recent photos. Therefore, when a new photo is added, check the number of recent photos, and remove the oldest one if there are to many:
#define RECENT_PHOTOS_MAX_NUMBER 20 +(Recent *)recentPhoto:(Photo *)photo { Recent *recent = nil; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Recent"]; request.predicate = [NSPredicate predicateWithFormat:@"photo = %@", photo]; NSError *error = nil; NSArray *matches = [photo.managedObjectContext executeFetchRequest:request error:&error]; if (!matches || ([matches count] > 1)) { // handle error } else if (![matches count]) { recent = [NSEntityDescription insertNewObjectForEntityForName:@"Recent" inManagedObjectContext:photo.managedObjectContext]; recent.photo = photo; recent.lastViewed = [NSDate date]; request.predicate = nil; request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"lastViewed" ascending:NO]]; matches = [photo.managedObjectContext executeFetchRequest:request error:&error]; if ([matches count] > RECENT_PHOTOS_MAX_NUMBER) { [photo.managedObjectContext deleteObject:[matches lastObject]]; } } else { recent = [matches lastObject]; recent.lastViewed = [NSDate date]; } return recent; }
We should update the recent entity only from the region-photos-table-view controller (not the recent-photos-table-view controller), best when we prepare the image view controller. Since this method is currently private to the generic photos-table-view controller we need to make it public:
- (void)prepareViewController:(id)vc forSegue:(NSString *)segueIdentifer fromIndexPath:(NSIndexPath *)indexPath;
Now we can overwrite it from within the region-photos-table-view controller:
- (void)prepareViewController:(id)vc forSegue:(NSString *)segueIdentifer fromIndexPath:(NSIndexPath *)indexPath { Photo *photo = [self.fetchedResultsController objectAtIndexPath:indexPath]; [Recent recentPhoto:photo]; [super prepareViewController:vc forSegue:segueIdentifer fromIndexPath:indexPath]; }
Finally, adjust the recent-photos-table-view controller to listen for the available database context. Set the fetched-results controller when the context is available:
@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext; ... - (void)awakeFromNib { [super awakeFromNib]; [[NSNotificationCenter defaultCenter] addObserverForName:PhotoDatabaseAvailabilityNotification object:nil queue:nil usingBlock:^(NSNotification *note) { self.managedObjectContext = note.userInfo[PhotoDatabaseAvailabilityContext]; }]; } - (void)setManagedObjectContext:(NSManagedObjectContext *)managedObjectContext { _managedObjectContext = managedObjectContext; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"]; request.predicate = [NSPredicate predicateWithFormat:@"recent != nil"]; request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"recent.lastViewed" ascending:NO]]; self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil]; }
The complete code for tasks #1 to #7 is available on github.