Question

I have a CoreData model that contains Levels which can again contain childLevels.

I represent each level with a UITableViewController listing all childLevels. When a user taps a row a new UITableViewController is pushed onto the navigationController. No problem here.

How would I go about storing the user location within this table structure. Is there a best practice for doing this? I have no problem doing this if the depth of the structure was known, but somehow puzzled how to approach this with a undefined depth.

Should I store the NSIndexPath tapped by the user into an array and write it to disk?

Was it helpful?

Solution 2

Instead of using the NSIndexPaths tapped by the user I went with the underlying NSManagedObjects which is a lot safer (in case number or sorting of objects change) and faster (because I do not need the whole fetchRequest and or view).

I subclassed the UINavigationController and did the following.

When pushing a new TableViewController for a level (stored in parentLevel) I append this to an array in UserDefaults:

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
   [super pushViewController:viewController animated:animated];

   if([viewController isKindOfClass:[LevelTableViewController class]]){
       NSMutableArray *array = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:LevelTablesPersistentKey]];
       NSManagedObject *obj = [(LevelTableViewController*)viewController parentLevel];

       if(obj!=nil){
         [array addObject:[[obj objectID].URIRepresentation absoluteString]];
       } 

       [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithArray:array] objectForKey:LevelTablesPersistentKey];

   }
}

When I pop a viewController I simply remove the last entry from that array:

- (UIViewController *) popViewControllerAnimated:(BOOL)animated{
  UIViewController *vc = [super popViewControllerAnimated:animated];
  // remove last object
  if([vc isKindOfClass:[LevelTableViewController class]]){
     NSMutableArray *array = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:LevelTablesPersistentKey]];
     [array removeLastObject];
     [[NSUserDefaults standardUserDefaults] setObject:[NSArray arrayWithArray:array] objectForKey:LevelTablesPersistentKey];
  }

  return vc;
}

I can then use this array when initializing the NavigationController when the app is next started to rebuild the tree:

- (LevelNavigationController*) initWithRootViewController:(LevelTableViewController*)vc {
if(self = [super initWithRootViewController:vc]){
    // Recreate structure from UserDefaults
    NSArray *array = [NSArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:LevelTablesPersistentKey]];
    [[NSUserDefaults standardUserDefaults] setObject:nil forKey:LevelTablesPersistentKey]; // set the array to nil -> will be rebuild when pushing viewcontrollers onto navigation stack

    NSPersistentStoreCoordinator *persistentStoreCoordinator = ...; // pointer to coordinator 
            NSManagedObjectContext * managedObjectContext = ...; // pointer to your context
    for (NSString *objId in array) {
        NSManagedObjectID *mobjId=[persistentStoreCoordinator managedObjectIDForURIRepresentation:[NSURL URLWithString:objId]];
        if(mobjId!=nil){

            NSManagedObject *obj = nil;
            NSError **err = nil;
            obj = [managedObjectContext objectWithID:mobjId];

            if(err==nil && obj){
                if([obj.entity.name isEqualToString:@"Level"]){
                    // push level

                    LevelTableViewController *nextLevel = [[LevelTableViewController alloc] initWithStyle:UITableViewStylePlain];
                    nextLevel.parentLevel = (Level*)obj;
                    [self pushViewController:nextLevel animated:NO];
                    [nextLevel release];
                } 
            } 
        }
    }

}

return self;

}

OTHER TIPS

Using an NSIndexPath for your state and saving/restoring it for persistence makes sense to me.

Also your approach--to use an NSArray stored as a property list (plist)--should be fairly straightforward.

I'm just about ready to start doing this for my own app and here's the plan. Each time a UIViewController loads a new view it will create a value in the NSUserDefaults that gives information on that new view (where it was opened from, what data it's populated with, etc). When the subview returns the view controller clears out this value. When the UIViewController has it's initial load it checks the defaults to see if it previously stored a value, if so, it takes the appropriate action to reload that subview. The process continues down the navigation chain.

The reason I've done this instead of an NSIndexPath is because, in addition to the main view hierarchy, I've got a bunch of auxiliary views (add, remove, edit, etc.). Not only will I need to record the opening of these views that exist outside of the main navigation, but they will have a lot of state details they need to save too (selected options, partially entered text, etc.)

I'll come back here and downvote this if it turns out to be a shit plan.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top