Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RestKit and saving to CoreData as NSManagedObject

I'm using RestKit and i want to parse and save elements to core data. I have two json files:

First (Category):

[
   {
     "cat_id": 3371,
     "cat_name": "myName",
     "image": 762
   },
   {
     "cat_id": 3367,
     "cat_name": "anotherName",
     "image": 617
   }
]

And second (Elements):

[
  {
    "art_id": "1",
    "node": {
      "author": "name"
    },
    "small_url": 0
  },
  {
    "art_id": "12",
    "node": {
      "author": "anotherName"
    },
    "small_url": 0
  }
]

So the basic idea is that every category have some elements inside. So this is my CoreData struct: enter image description here

I've download the restkit example and use TwitterCoreData sample. My code is: AppDelegeta.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSURL *baseURL = [NSURL URLWithString:@"http://globalURL.com"];
    RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:baseURL];
    NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
    RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
    objectManager.managedObjectStore = managedObjectStore;

    RKEntityMapping *categoryMapping = [RKEntityMapping mappingForEntityForName:@"Category" inManagedObjectStore:managedObjectStore];
    categoryMapping.identificationAttributes = @[ @"catId" ];
    [categoryMapping addAttributeMappingsFromDictionary:@{
     @"cat_id": @"catId",
     @"node.author": @"author",
     }];

    RKEntityMapping *elementsMapping = [RKEntityMapping mappingForEntityForName:@"Elements" inManagedObjectStore:managedObjectStore];
    elementsMapping.identificationAttributes = @[ @"artId" ];
    [elementsMapping addAttributeMappingsFromDictionary:@{
     @"art_id": @"artId",
     @"node.author": @"author",
    }];
    [elementsMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"category" toKeyPath:@"category" withMapping:categoryMapping]];
    RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:elementsMapping
                                                                                       pathPattern:nil
                                                                                           keyPath:nil
                                                                                       statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
    [objectManager addResponseDescriptor:responseDescriptor];


    [managedObjectStore createPersistentStoreCoordinator];
    NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"MyCoreData.sqlite"];
    NSString *seedPath = [[NSBundle mainBundle] pathForResource:@"MyCoreData" ofType:@"sqlite"];
    NSError *error;
    NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
    NSAssert(persistentStore, @"Failed to add persistent store with error: %@", error);

    // Create the managed object contexts
    [managedObjectStore createManagedObjectContexts];

    // Configure a managed object cache to ensure we do not create duplicate objects
    managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];



    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.viewController = [[ViewController alloc] initWithNibName:@"ViewController" bundle:nil];
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    return YES;
}

and ViewController.m:

NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Elements"];
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"artId" ascending:NO];
fetchRequest.sortDescriptors = @[descriptor];
[[RKObjectManager sharedManager] getObjectsAtPath:@"/detailaddress/:catId" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
    RKLogInfo(@"Load complete: Table should refresh...");
    NSLog(@"%@",mappingResult);
    [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:@"LastUpdatedAt"];
    [[NSUserDefaults standardUserDefaults] synchronize];
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
    RKLogError(@"Load failed with error: %@", error);
}];

And log for mapping show me the "nil". How to save data from my first json (category) into core data using restkit? Rememnber that i don't have Elements list yet.

When i use create new file to create NEManagedObject subclass i've got Elements class.

@interface Elements : NSManagedObject

@property (nonatomic, retain) NSNumber * artId;
@property (nonatomic, retain) NSString * author;
@property (nonatomic, retain) NSString * title;
@property (nonatomic, retain) NSManagedObject *category;

@end
like image 271
Jakub Avatar asked Feb 13 '13 13:02

Jakub


1 Answers

I used RESTKit until the .20 release a few months ago. In all honesty, while this library has great intentions, in my experience it ends up wasting far more time than it was ever meant to save me.

I have used it with API's created with .NET/MVC, PHP, python/Django/TastyPie, and hacked it to do some stuff with Twitter. This all ended up being a very interesting academic exercise, but in the end was little more than that. Both the old/new versions of RESTKit assume ALOT about the way your API is going to be responding to requests.

With the .10 release, there was a fair amount of customizability through the use of Obj-C Blocks. I could intercept the request/response of an RKRequest and pretty much override anything that RESTKit was going to do. This seemed fairly awesome. Now with the .20 release, everything got very... clever (clever code is usually not good).

Instead of trying to remain generic and malleable, RK now does all kinds of "convenient" things for you. This effectively takes away your ability to jam it into whatever (non-idealized and realistic) shape your API has taken. At this point, I have gone back to writing all of my code using AFNetworking. I'd rather take the extra time to write my own parsing code, and know that when my Unit Tests pass, I'm done. NOT spend hours trying to get RK to log out meaningful error messages.

The other real problem with RK was that it does not account for being offline. It was designed with the assumption that your application is ALWAYS online, and simply mirrors a persistant store in the sky. My applications all handle content-creation on an iOS device, which MAY OR MAY NOT be online at the time of content-creation. This is the main reason I was "customizing" RK .10. When I saw the changes in .20, I decided enough was enough, and went back to doing things the old way.

Maybe I'll just write my own CRUD/RESTful framework, cause I'm starting to get tired of spending time using very specialized libraries, that try to do everything with as little responsibility being left to the application developer as possible. Frameworks like this tend to be Swiss Army Knives. They look really awesome on paper, THEY CAN DO ANYTHING, then you actually try to cut something and the blade is too dull or breaks off. Good software does very few things very well, and if it sounds too good to be true, it probably is.

like image 92
G. Shearer Avatar answered Oct 24 '22 08:10

G. Shearer