An educational assignment with little help from the tutorial. You have to follow apple’s documentation, ray’s wenderlich blog and many others…
You can read my code (without warranty :))
//Paparazzi2AppDelegate.h #import <UIKit/UIKit.h> @class FlickrFetcher; @class PersonListViewController; @interface Paparazzi2AppDelegate : NSObject <UIApplicationDelegate> { FlickrFetcher *flickrFetcher; NSManagedObjectContext *moc; UITabBarController *tabBarController; UINavigationController *personListNavController; UINavigationController *recentsViewNavController; PersonListViewController *personListViewController; } @property (nonatomic, retain) IBOutlet UIWindow *window; - (void)populateCoraData; @end // Paparazzi2AppDelegate.m #import "Paparazzi2AppDelegate.h" #import "FlickrFetcher.h" #import "Photo.h" #import "Person.h" #import "PersonListViewController.h" @implementation Paparazzi2AppDelegate @synthesize window=_window; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Instantiate the 'singleton' instance of FlickrFetcher class flickrFetcher = [FlickrFetcher sharedInstance]; // Check if the database already exists if (![flickrFetcher databaseExists]) { [self populateCoraData]; } else { moc = [flickrFetcher managedObjectContext]; } tabBarController = [[UITabBarController alloc] init]; personListNavController = [[UINavigationController alloc] init]; personListViewController = [[PersonListViewController alloc] initWithStyle:UITableViewStylePlain]; [personListNavController pushViewController:personListViewController animated:NO]; [PersonListViewController release]; recentsViewNavController = [[UINavigationController alloc] init]; [tabBarController setViewControllers:[NSArray arrayWithObjects:personListNavController, recentsViewNavController, nil] animated:NO]; [_window addSubview:[tabBarController view]]; [self.window makeKeyAndVisible]; return YES; } - (void)populateCoraData { NSError *error = nil; moc = [flickrFetcher managedObjectContext]; // Get the path to copy the plist file to docments directory NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@"FakeData.plist"]; // Check if the plist file is already in the documents directory NSFileManager *fileManager = [[NSFileManager alloc] init]; NSArray *plistData = [NSArray arrayWithContentsOfFile:plistPath]; if ([fileManager fileExistsAtPath:plistPath] == NO) { NSLog(@"Initial installation:retrieve and copy FakeData.plist from the main bundle"); NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"FakeData" ofType:@"plist"]; plistData = [NSArray arrayWithContentsOfFile:bundlePath]; if (plistPath) { [fileManager copyItemAtPath:bundlePath toPath:plistPath error:&error]; } } [fileManager release]; NSLog(@"Now importing values to the entities"); // A mutable array that holds the string names of the persons NSMutableArray *names = [[NSMutableArray alloc] init]; for (NSDictionary *item in plistData) { Photo *photo = [NSEntityDescription insertNewObjectForEntityForName:@"Photo" inManagedObjectContext:moc]; [photo setName:[item objectForKey:@"name"]]; [photo setImage:[item objectForKey:@"path"]]; // Check if the photographer's name has already been set for a Person object NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ IN %@", [item objectForKey:@"user"], names]; if ([predicate evaluateWithObject:item] == NO) { Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:moc]; [person setUserName:[item objectForKey:@"user"]]; [photo setPerson:person]; [names addObject:[item objectForKey:@"user"]]; NSLog(@"I have inserted the photo:\"%@\" of new photographer: %@", [photo name], [person userName]); } else { NSArray *objectArray = [flickrFetcher fetchManagedObjectsForEntity:@"Person" withPredicate:predicate]; Person *person = [objectArray objectAtIndex:0]; [photo setPerson:person]; NSLog(@"I have inserted the photo \"%@\" of existing photographer: %@", [photo name], [person userName]); } } [names release]; } - (void)applicationWillResignActive:(UIApplication *)application { /* Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. */ } - (void)applicationDidEnterBackground:(UIApplication *)application { if (moc != nil) { if ([moc hasChanges] == YES) { NSError *error = nil; if (![moc save:&error]) { NSLog(@"Error while saving\n%@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error"); exit(1); } } } } - (void)applicationWillEnterForeground:(UIApplication *)application { /* Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. */ } - (void)applicationDidBecomeActive:(UIApplication *)application { /* Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. */ } - (void)applicationWillTerminate:(UIApplication *)application { if (moc != nil) { if ([moc hasChanges] == YES) { NSError *error = nil; if (![moc save:&error]) { NSLog(@"Error while saving\n%@", ([error localizedDescription] != nil) ? [error localizedDescription] : @"Unknown Error"); exit(1); } } } //[self saveContext]; } - (void)dealloc { [_window release]; [flickrFetcher release]; [moc release]; [personListNavController release]; [recentsViewNavController release]; [tabBarController release]; [super dealloc]; } @end
// PersonListViewController.h #import <UIKit/UIKit.h> @class PhotoListViewController; @interface PersonListViewController : UITableViewController <NSFetchedResultsControllerDelegate, UITableViewDelegate> { NSManagedObjectContext *moc; NSFetchedResultsController *_fetchedResultsController; } @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController; @end // // PersonListViewController.m // Paparazzi2 // // Created by Tiger on 05/08/11. // Copyright 2011 __MyCompanyName__. All rights reserved. // #import "PersonListViewController.h" #import "PhotoListViewController.h" #import "FlickrFetcher.h" #import "Person.h" #import "Photo.h" @implementation PersonListViewController @synthesize fetchedResultsController = _fetchedResultsController; - (NSFetchedResultsController *)fetchedResultsController { // Checks to see if the fetched results controller exists first. // If it does exist, it will return it, otherwise it will create it. if (_fetchedResultsController != nil) { return _fetchedResultsController; } // Create the request for the persons NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:moc]; [fetchRequest setEntity:entity]; // Set the sort descriptor NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"userName" ascending:YES]; NSArray *sortDescriptors = [NSArray arrayWithObject:sort]; [fetchRequest setSortDescriptors:sortDescriptors]; [fetchRequest setFetchBatchSize:20]; // Set up the Fetched Results Controller NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc sectionNameKeyPath:nil cacheName:@"Root"]; managedObjectContext:moc sectionNameKeyPath:nil cacheName:@"Root"]; [self setFetchedResultsController:theFetchedResultsController]; [_fetchedResultsController setDelegate:self]; [sort release]; [fetchRequest release]; [theFetchedResultsController release]; return _fetchedResultsController; } - (id)initWithStyle:(UITableViewStyle)style { self = [super initWithStyle:style]; if (self) { } return self; } - (void)dealloc { [moc release]; [_fetchedResultsController release]; [super dealloc]; } - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } #pragma mark - View lifecycle - (void)viewDidLoad { [super viewDidLoad]; [self setTitle:@"Contacts"]; FlickrFetcher *flickrFetcher = [FlickrFetcher sharedInstance]; moc = [flickrFetcher managedObjectContext]; NSError *error = nil; if (![[self fetchedResultsController] performFetch:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); exit(-1); // Fail } } - (void)viewDidUnload { [super viewDidUnload]; [self setFetchedResultsController:nil]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return (interfaceOrientation == UIInterfaceOrientationPortrait); } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [[_fetchedResultsController sections] count]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { id <NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section]; return [sectionInfo numberOfObjects]; } - (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { Person *person = [_fetchedResultsController objectAtIndexPath:indexPath]; [[cell textLabel] setText:[person userName]]; NSSet *personsPhotos = [person photo]; Photo *image = [personsPhotos anyObject]; [[cell imageView] setImage:[UIImage imageNamed:[image image]]]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } [self configureCell:cell atIndexPath:indexPath]; return cell; } #pragma mark - Table view delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { PhotoListViewController *photoListViewController = [[PhotoListViewController alloc] initWithStyle:UITableViewStylePlain]; Person *person = [_fetchedResultsController objectAtIndexPath:indexPath]; [photoListViewController setPerson:person]; [self.navigationController pushViewController:photoListViewController animated:YES]; [photoListViewController release]; } #pragma mark - NSFetchedResultsController delegate methods // Just copied and pasted these from an Apple sample - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { [self.tableView beginUpdates]; } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { switch(type) { case NSFetchedResultsChangeInsert: [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; } } - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { UITableView *tableView = self.tableView; switch(type) { case NSFetchedResultsChangeInsert: [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeUpdate: [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; break; case NSFetchedResultsChangeMove: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; } } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { [self.tableView endUpdates]; } @end
Assume that Person and Photo classes are auto-generated from xcode.
It’s worth saying that I got help from MI Postel blog