如何使用RestKit将嵌套的JSON对象存储到Core Data中

时间:2014-04-16 14:22:34

标签: ios objective-c json core-data restkit

我正在尝试实现我认为非常简单的用例,但我不确定如何正确使用它。

当我的应用程序加载特定屏幕时,我需要调用一个返回嵌套JSON对象的webservice,然后解析所有数据并保存到一系列Core Data托管对象中。每当用户离开并返回该屏幕时,我想用从服务器返回的新数据替换当前Core Data存储中的所有数据。

这是高级概述。看起来很简单。

在app load上调用

setupRestKit

+ (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];
}

第25行的NSArray *descriptors填充了一系列描述符,其中包括以下返回的描述符对象:

+ (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;
}

这些描述符和关系完全包含我的嵌套JSON对象,我相信它们可以正常工作。

我想,我的问题在于RestKit和Core Data如何协同工作。我对这两种技术都比较陌生。以下代码段描述了我的ViewController用于从Web服务获取数据的内容:

// 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];
}

在编译应用程序并首次运行时,最后一个代码段中的schedule正确加载了嵌套的数据数组。但是对getData的任何后续调用都会导致核心数据“错误”。

我应该致电getObjectsAtPath:parameters:success:failure吗?我应该在哪些上下文中存储和删除我的Schedule数据?有人能指出我正确的方向吗? RestKit现在有点压倒我。

1 个答案:

答案 0 :(得分:1)

您的代码通常应使用mainQueueManagedObjectContext,以便看起来不错。

如果要保存,则应在上下文中调用saveToPersistentStore:而不是save:

是的,您应该致电getObjectsAtPath:parameters:success:failure,因为这是从服务器获取数据的原因。

您不需要自己删除对象,请查看fetch request blocks