8

I'm curious to know what the best way is to create a new NSManagedObject in RestKit 0.20? Currently my code looks something like this:

#pragma mark - navigation buttons

- (void)createButtonDidTouch
{
    // create new album object    
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    NSManagedObjectContext *parentContext = RKObjectManager.sharedManager.managedObjectStore.mainQueueManagedObjectContext;
    context.parentContext = parentContext;
    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Album" inManagedObjectContext:parentContext];
    Album *newAlbum = [[Album alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:context];

    // pass object to create view to manipulate
    AlbumCreateViewController *createViewController = [[AlbumCreateViewController alloc] initWithData:newAlbum];
    createViewController.delegate = self;
    createViewController.managedObjectContext = context;

    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:createViewController];
    navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;

    [self presentViewController:navController animated:YES completion:nil];
}

#pragma mark - create view controller delegate

- (void)createViewControllerDidSave:(NSManagedObject *)data
{
    // dismiss the create view controller and POST

    // FIXME: add restkit code to save the object
    NSLog(@"save the object...");

    NSDictionary *userInfo = [KeychainUtility load:@"userInfo"];
    NSString *path = [NSString stringWithFormat:@"/albums/add/%@/%@", userInfo[@"userID"], userInfo[@"apiKey"]];

    [RKObjectManager.sharedManager postObject:data path:path parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
        operation.targetObject = data;
    } failure:^(RKObjectRequestOperation *operation, NSError *error) {
        NSLog(@"create album error: %@", error);
    }];

    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)createViewControllerDidCancel:(NSManagedObject *)data
{
    // dismiss the create view controller

    NSLog(@"delete the object...");
    // FIXME: add restkit code to delete the object

    [self dismissViewControllerAnimated:YES completion:nil];
}

I'm also curious to know what my responsibilities are for saving / deleting this object. If I POST to the server via RestKit is the managed object context saved?

What if I decide to cancel this creation process — what's the preferred way to delete this object?

Basically how much is RestKit doing for me, and what should I make sure I'm doing. I haven't found much documentation on this and would like to be clear on it.

David
  • 9,635
  • 5
  • 62
  • 68
rosem
  • 1,311
  • 14
  • 19

2 Answers2

11

When you initialize an RKManagedObjectRequestOperation for a given object, RestKit will obtain a permanent object ID for that object and then create a child managed object context whose parent context is the context the object is inserted into. The operation then executes the HTTP request to completion and obtains a response.

If the response is successful and the mapping of the response is successful (note that the mapping occurs within this private child context), then the private child context is saved. The type of save invoked is determined by the value of the savesToPersistentStore property (see http://restkit.org/api/0.20.0/Classes/RKManagedObjectRequestOperation.html#//api/name/savesToPersistentStore).

When YES, the context is saved recursively all the way back to the persistent store via the NSManagedObjectContext category method saveToPersistentStore (see http://restkit.org/api/0.20.0/Categories/NSManagedObjectContext+RKAdditions.html).

When NO, the context is saved via a vanilla [NSManagedObjectContext save:] message, which 'pushes' the changes back to the parent context. They will remain local to that context until you save them back. Keep in mind that managed object context parent/child hierarchies can be as long as you create within the application.

If the HTTP request failed or there was an error during the mapping process, the private context is not saved and the operation is considered failed. This means that no changes are saved back to the original MOC, leaving your object graph just as it was before the operation was started (except the object being sent, if temporary, now has a permanent object ID but is still unsaved).

Blake Watters
  • 6,607
  • 1
  • 43
  • 33
  • How would I delete this object then — assuming I created the object with the above code, but didn't fire the POST request — rather canceled/close the "create" view controller? Just through my local NSMangedObjectContext? – rosem Dec 06 '12 at 21:25
  • 3
    In the above code its been inserted into the context, but unsaved. You could delete it from the context on dismissal of the view controller. But I would recommend that you **NOT** insert objects directly into the `managedObjectStore.mainQueueManagedObjectContext`, but instead create a child managed object context when you are segueing to the creation controller and assign it to the controller. What you can then do is just let the MOC dealloc when you cancel. – Blake Watters Dec 06 '12 at 21:42
  • is there a shortcut to create the child context? before I remember using something like [Album createObject] — or something similar. – rosem Dec 06 '12 at 22:27
  • NSManagedObjectContext *managedObjectContext = [[NSManagedContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; managedObjectContext.parentContext = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext; – Blake Watters Dec 06 '12 at 22:49
  • 1
    If you adopt the "pass the baton" approach, the `parentContext` would be set to self.managedObjectContext and then you'd assign that context to `childViewController.managedObjectContext`. In this way you'll wind up with a hierarchy of contexts that matches your navigation stack. If the child is only a show view, you can just pass in self.managedObjectContext. This gives you flexibility and isolation for handling errors, canceling by discarding the context, etc. – Blake Watters Dec 06 '12 at 22:53
  • great! thank you! is `operation.targetObject = data;` still correct for mapping the response back to my object? – rosem Dec 06 '12 at 23:15
  • also, what about the actual album creation though? is that done in the parent or child view controller? `KeepsaykAlbum *newAlbum = [[KeepsaykAlbum alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:context];` – rosem Dec 06 '12 at 23:28
  • ok, closer now... I updated the code above... it's all working an posting except I don't see the newly create object in my table view until I refresh with a GET request... – rosem Dec 06 '12 at 23:48
  • 4
    Could really use a flushed out code sample showing how to create and save a CoreData object with RestKit. The "Creating Gists" section is empty in the RKGist tutorial. I don't know where else to look. – Sean Rucker Sep 05 '13 at 15:24
  • The links in your answer now all point to the main github repository :-( – Besi Aug 23 '15 at 04:20
3

The way you do it should works (calling each time the MOC in each of your VC), but is not "recommended".

What Apple suggests, just like any Core Data app, is the "pass the baton" style.

Nested contexts make it more important than ever that you adopt the “pass the baton” approach of accessing a context (by passing a context from one view controller to the next) rather than retrieving it directly from the application delegate.

See here: http://developer.apple.com/library/ios/#releasenotes/DataManagement/RN-CoreData/_index.html

As for your second question, RestKit should manage saving/updating your Core Data stack upon success of your api calls if everything is well mapped/setup.

From blake the RK creator:

if you POST or PUT a Core Data object, RK obtains a permanent object ID for it and then creates a secondary managed object context, fires the request, and maps the response (if possible). if the response and the mapping are successful, it will either save it back to the parent context or all the way back to the persistent store (i.e. into SQLite) based on the value of the savesToPersistentStore.

allaire
  • 5,995
  • 3
  • 41
  • 56