Question

I’m trying to implement what I believe to be a pretty simple use case, but I’m not sure how to correctly go about it.

When a particular screen loads in my app, I need to call a webservice which returns a nested JSON object, then parse all the data and save into a series of Core Data managed objects. Whenever the user goes leaves and goes back to that screen, I want to replace all the data currently in my Core Data store with new data coming back from the server.

That’s the high level overview. Seems pretty simple.

setupRestKit is called on app load:

+ (void)setupRestKit
{
    RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:BASE_URL]];
    [RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:@"application/vnd.collection+json"];

    NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"Mobile" ofType:@"momd"]];
    NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
    RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];

    [managedObjectStore createPersistentStoreCoordinator];

    NSString *documentPath = [NIAppDelegate applicationDocumentsDirectory].relativePath;
    NSError *error;
    [managedObjectStore addSQLitePersistentStoreAtPath:[NSString stringWithFormat:@"%@/Mobile.sqlite", documentPath]
                                fromSeedDatabaseAtPath:nil
                                     withConfiguration:nil
                                               options:nil
                                                 error:&error];

    [managedObjectStore createManagedObjectContexts];

    manager.managedObjectStore = managedObjectStore;

    NSArray *rkClasses = [self allNIRestKitClasses];
    NSArray *descriptors = [self allDescriptorsFromRKClasses:rkClasses];

    [manager addResponseDescriptorsFromArray:descriptors];
}

The NSArray *descriptors on line 25 is populated with an array of descriptors, which includes the following returned descriptor object:

+ (RKResponseDescriptor *)getDescriptor
{
    RKManagedObjectStore *store = [RKObjectManager sharedManager].managedObjectStore;

    RKEntityMapping *scheduleMapping = [RKEntityMapping mappingForEntityForName:@"Schedule" inManagedObjectStore:store];
    [scheduleMapping addAttributeMappingsFromDictionary:@{@"version": @"version",
                                                      @"href": @"href"}];

    RKEntityMapping *scheduleItemMapping = [RKEntityMapping mappingForEntityForName:@"ScheduleItem" inManagedObjectStore:store];
    [scheduleItemMapping addAttributeMappingsFromDictionary:@{@"href": @"href"}];

    RKEntityMapping *scheduleDataMapping = [RKEntityMapping mappingForEntityForName:@"ScheduleData" inManagedObjectStore:store];
    [scheduleDataMapping addAttributeMappingsFromDictionary:@{@"AllCurricConfirmed": @"allCurricConfirmed",
                                                              @"EndDateTime": @"endDateTime",
                                                              @"EventLocation": @"eventLocation",
                                                              @"Notes": @"notes",
                                                              @"RecordID": @"recordID",
                                                              @"ScheduleType": @"scheduleType",
                                                              @"StartDateTime": @"startDateTime",
                                                              @"Title": @"title"}];

    RKEntityMapping *scheduleLinkMapping = [RKEntityMapping mappingForEntityForName:@"ScheduleLink" inManagedObjectStore:store];
    [scheduleLinkMapping addAttributeMappingsFromDictionary:@{@"rel": @"rel",
                                                              @"href": @"href"}];

    RKRelationshipMapping *scheduleLinksInScheduleItem = [RKRelationshipMapping relationshipMappingFromKeyPath:@"link"
                                                                                                     toKeyPath:@"scheduleLinks"
                                                                                                   withMapping:scheduleLinkMapping];
    [scheduleItemMapping addPropertyMapping:scheduleLinksInScheduleItem];

    RKRelationshipMapping *scheduleDatasInScheduleItem = [RKRelationshipMapping relationshipMappingFromKeyPath:@"data"
                                                                                                 toKeyPath:@"scheduleDatas"
                                                                                               withMapping:scheduleDataMapping];
    [scheduleItemMapping addPropertyMapping:scheduleDatasInScheduleItem];

    RKRelationshipMapping *scheduleItemsInSchedule = [RKRelationshipMapping relationshipMappingFromKeyPath:@"items"
                                                                                                 toKeyPath:@"scheduleItems"
                                                                                               withMapping:scheduleItemMapping];
    [scheduleMapping addPropertyMapping:scheduleItemsInSchedule];

    NSIndexSet *scheduleStatusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
    RKResponseDescriptor *scheduleDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:scheduleMapping
                                                                                         method:RKRequestMethodGET
                                                                                    pathPattern:nil
                                                                                        keyPath:@"collection"
                                                                                    statusCodes:scheduleStatusCodes];

    return scheduleDescriptor;
}

Those descriptors and relationships completely encompass my nested JSON object, and I believe them to work correctly.

My question, I guess, is with how RestKit and Core Data work together. I’m relatively new to both of these technologies. The following snippet describes what my ViewController is doing to get data from the webservice:

// ViewController.m
- (void)getData
{
    [SVProgressHUD showWithStatus:@"Downloading Schedule"];

    [NIRestKitSchedule getScheduleWithBlock:^(BOOL valid) {
        if (valid) {
            // get the data from Core Data (because I assume that RestKit has saved everything into it already?)
            NSManagedObjectContext *mainContext = RKObjectManager.sharedManager.managedObjectStore.mainQueueManagedObjectContext;
            NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:@"Schedule"];
            NSArray *schedule = [mainContext executeFetchRequest:fetch error:nil];

            // TODO: and do stuff with it

            [SVProgressHUD dismiss];
        } else {
            [SVProgressHUD showErrorWithStatus:@"Could not get Schedule"];
        }
    }];
}

// NIRestKitSchedule.m
+ (void)getScheduleWithBlock:(void (^)(BOOL))valid
{
    [self deleteEntity:@"Schedule"];

    NSURL *scheduleUrl = [RKObjectManager sharedManager].baseURL;

    scheduleUrl = [scheduleUrl URLByAppendingPathComponent:SCHEDULE_ENDPOINT];

    scheduleUrl = [scheduleUrl URLByAppendingDefaultParameters];
    [[RKObjectManager sharedManager] getObjectsAtPath:[scheduleUrl absoluteString]
                                           parameters:nil
                                              success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                                                  valid(YES);
                                              }
                                              failure:^(RKObjectRequestOperation *operation, NSError *error) {
                                                  valid(NO);
                                              }];

}

+ (void)deleteEntity:(NSString *)entityName
{
    NSManagedObjectContext *mainContext = [RKObjectManager sharedManager].managedObjectStore.mainQueueManagedObjectContext;

    NSFetchRequest *fetchObjects = [[NSFetchRequest alloc] init];
    [fetchObjects setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:mainContext]];
    [fetchObjects setIncludesPropertyValues:NO];

    NSError *error = nil;
    NSArray *objects = [mainContext executeFetchRequest:fetchObjects error:&error];

    for (NSManagedObject *object in objects) {
        [mainContext deleteObject:object];
    }
    NSError *saveError = nil;
    [mainContext save:&saveError];
}

Upon compiling the app and running for the first time, schedule in the last code snippet is correctly loaded with the nested array of data. But any subsequent calls to getData result in a Core Data “fault".

Should I be calling getObjectsAtPath:parameters:success:failure? Which contexts should I be storing and deleting my Schedule data into and from? Can someone point me in the right direction? RestKit is kind of overwhelming me right now.

Was it helpful?

Solution

Your code should generally be using mainQueueManagedObjectContext so that looks fine.

When you want to save, you should be calling saveToPersistentStore: instead of save: on the context.

Yes, you should be calling getObjectsAtPath:parameters:success:failure because that's what gets the data from the server.

You shouldn't need to delete objects yourself, check out fetch request blocks.

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