RestKit 0.20 - Dynamic nested object mapping - objective-c

I'm working on Objective C project, that uses RestKit framework for parsing JSON responses.
Now I need some help with object mapping settings for following case:
JSON response:
{
"data": {
"SOME.API.Auth": {
"maxVersion": 2,
"minVersion": 1,
"path": "auth.cgi"
},
"SOME.Station": {
"maxVersion": 1,
"minVersion": 1,
"path": "Station/task.cgi"
}
},
"success": true
}
and following objects:
#interface Response : NSObject
#property (strong, nonatomic) NSArray *data;
#property (assign, nonatomic) BOOL success;
#end
#interface SomeAPIInfo : NSObject
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) NSString *path;
#property (strong, nonatomic) NSString *minVersion;
#property (strong, nonatomic) NSString *maxVersion;
#end
And here is my mapping settings:
RKObjectMapping *responseMapping = [RKObjectMapping mappingForClass:[Response class]];
[responseMapping addAttributeMappingsFromDictionary:#{#"success": #"success"}];
RKObjectMapping *dataObjectMapping = [RKObjectMapping mappingForClass:[SomeAPIInfo class]];
dataObjectMapping.forceCollectionMapping = YES;
[dataObjectMapping addAttributeMappingFromKeyOfRepresentationToAttribute:#"name"];
[dataObjectMapping addAttributeMappingsFromDictionary:#{
#"(name).path": #"path",
#"(name).minVersion": #"minVersion",
#"(name).maxVersion": #"maxVersion"}];
[responseMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"data"
toKeyPath:#"data"
withMapping:dataObjectMapping]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping
pathPattern:nil
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[_objectManager addResponseDescriptor:responseDescriptor];
Problem is that "data" object is not properly mapped:
NSArray *data;
is filled with 2 "SomeAPIInfo" objects. name is properly filled, but other values(path,maxVersion,minVersion) are empty.
What am I doing wrong ?
Is there another way to map "data" object ? Maybe directly into NSDictionary, so "Some.API.Auth" would be key and "SomeAPIInfo" would be object (without "name" property).
Thanks for help!
Edit: I think that the mapping doesn't work because of the dots in key ("SOME.API.Auth).
RKMappingOperation.m:
- (NSArray *)simpleAttributeMappings
{
NSMutableArray *mappings = [NSMutableArray array];
for (RKAttributeMapping *mapping in self.nestedAttributeMappings) {
if ([mapping.sourceKeyPath rangeOfString:#"."].location == NSNotFound) {
[mappings addObject:mapping];
}
}

I ran into similar issues that was caused by a bug in RestKit https://github.com/RestKit/RestKit/issues/1532 . I'd try to eliminate the possible dot issue by creating a fake JSON response with the same structure but without the dots and see if the problem is still there.

Related

RestKit RKMappingResult gives [__NSCFBoolean length]: unrecognized selector sent to instance

I am fetching data from the New York Times Bestsellers JSON API using Reskit. I believe I have an issue with my attributes mapping. A typical JSON object that has to be fetched looks like the structure below. My code is also shown. The API call does return objects as matching the number of expected results but the RKMappingResult in the requestDataFromAPI method returns "[__NSCFBoolean length]: unrecognized selector sent to instance". I am not able to access them as printing Books gives nil. I am not sure what I'm doing wrong.
{
"status":"OK",
"copyright":"Copyright (c) 2016 The New York Times Company. All Rights Reserved.",
"num_results":10,
"last_modified":"2016-03-04T13:12:31-05:00",
"results":
{
"list_name":"Animals",
"bestsellers_date":"2016-02-27",
"published_date":"2016-03-13",
"display_name":"Animals",
"normal_list_ends_at":10,
"updated":"MONTHLY",
"books": [
{"rank":1,
"rank_last_week":0,
"weeks_on_list":0,
"asterisk":0,
"dagger":0,
"primary_isbn10":"0802123414",
"primary_isbn13":"9780802123411",
"publisher":"Grove Atlantic",
"description":"A grief-stricken British woman decides to raise a goshawk, a fierce bird that is notoriously difficult to tame.",
"price":0,
"title":"H IS FOR HAWK","author":"Helen Macdonald",
"contributor":"by Helen Macdonald",
"isbns": [
{"isbn10":"0802123414",
"isbn13":"9780802123411"
},
{"isbn10":"1448130727",
"isbn13":"9781448130726"
},
{"isbn10":"1481530968",
"isbn13":"9781481530965"
},
{"isbn10":"148153095X",
"isbn13":"9781481530958"
},
{"isbn10":"1410483614",
"isbn13":"9781410483614"
},
{"isbn10":"0802124739",
"isbn13":"9780802124739"
}]
}
}
- (void) initializeRestAPI
{
// Initialize RestKit using API base address
NSURL * baseURL = [NSURL URLWithString:#"http://api.nytimes.com"];
RKObjectManager * objectManager = [RKObjectManager managerWithBaseURL:baseURL];
// Initialize Core Data's managed object model from the bundle
NSManagedObjectModel * managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
// Initialize RestKit's managed object store
RKManagedObjectStore * managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
objectManager.managedObjectStore = managedObjectStore;
// Complete Core Data stack initialization via RestKit
[managedObjectStore createPersistentStoreCoordinator];
NSString * persistentStorePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:#"DataModel.sqlite"];
NSString * seedDatabasePath = [[NSBundle mainBundle] pathForResource:#"RKSeedDatabase" ofType:#"sqlite"];
NSError * error;
NSPersistentStore * persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:persistentStorePath fromSeedDatabaseAtPath:seedDatabasePath withConfiguration:nil options:nil error:&error];
NSAssert(persistentStore, #"Failed to add persistent store with error: %#", error);
// Create RestKit's managed object contexts
[managedObjectStore createManagedObjectContexts];
// Configure a managed object cache
managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
[self setupEntityMappingForObjectStore:managedObjectStore withObjectManager:objectManager];
[self requestDataFromAPI];
}
-(void) setupEntityMappingForObjectStore: (RKManagedObjectStore *) managedObjectStore withObjectManager: (RKObjectManager *) objectManager
{
RKEntityMapping * bookListMapping = [RKEntityMapping mappingForEntityForName:#"BookList" inManagedObjectStore:managedObjectStore];
bookListMapping.identificationAttributes = #[#"listName"];
[bookListMapping addAttributeMappingsFromDictionary:
#{#"results.list_name": #"listName",
#"results.bestsellers_date": #"bestsellersDate",
#"results.published_date": #"publishedDate",
#"results.display_name": #"displayName",
#"results.normal_list_ends_at": #"normalListEndsAt",
#"results.updated": #"updated"
}];
RKEntityMapping * bookMapping = [RKEntityMapping mappingForEntityForName:#"Book" inManagedObjectStore:managedObjectStore];
bookMapping.identificationAttributes = #[#"title"];
[bookMapping addAttributeMappingsFromDictionary:
#{#"rank": #"rank",
#"rank_last_week": #"rankLastWeek",
#"weeks_on_list": #"weeksOnList",
#"primary_isbn10": #"primaryIsbn10",
#"primary_isbn13": #"primaryIsbn13",
#"amazon_product_url": #"productUrl",
#"book_image": #"bookImage",
#"publisher": #"publisher",
#"description": #"bookDescription",
#"title": #"title",
#"contributor": #"contributor",
#"author": #"author",
#"price": #"price"
}];
[bookListMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"results.books" toKeyPath:#"books" withMapping:bookMapping]];
RKResponseDescriptor * bookListResponseDescriptor =
[RKResponseDescriptor responseDescriptorWithMapping:bookListMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:#"results.books"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)
];
[objectManager addResponseDescriptor:bookListResponseDescriptor];
// Enable Activity Indicator Spinner
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
}
- (void)fetchBooksFromContext
{
NSManagedObjectContext * context = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
NSFetchRequest * fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"BookList"];
NSSortDescriptor * descriptor = [NSSortDescriptor sortDescriptorWithKey:#"listName" ascending:YES];
fetchRequest.sortDescriptors = #[descriptor];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
BookList * bookList = [fetchedObjects firstObject];
NSArray * books = [bookList.books allObjects];
//NSArray * books = [fetchedObjects firstObject];
NSLog(#"Books: %#",books);
}
- (void)requestDataFromAPI
{
NSDictionary * apiKeyData = [[NSUserDefaults standardUserDefaults] objectForKey:#"apiKeyData"];
NSString * apiKey = [apiKeyData objectForKey:#"apiKeyData"];
NSLog(#"requestDataFromAPI apiKey: %#",apiKey);
NSString * requestPath = [[NSString alloc] initWithFormat:#"/svc/books/v3/lists/%#?&api-key=%#",_categoryListName, apiKey];
[[RKObjectManager sharedManager]
getObjectsAtPath:requestPath
parameters:nil
success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{
[self fetchBooksFromContext];
}
failure: ^(RKObjectRequestOperation *operation, NSError *error)
{
RKLogError(#"Loading from API failed with error: %#", error);
}
];
}
The core data object models are as shown
#import "BookList.h"
NS_ASSUME_NONNULL_BEGIN
#interface BookList (CoreDataProperties)
#property (nullable, nonatomic, retain) NSString *listName;
#property (nullable, nonatomic, retain) NSDate *bestsellersDate;
#property (nullable, nonatomic, retain) NSDate *publishedDate;
#property (nullable, nonatomic, retain) NSString *displayName;
#property (nullable, nonatomic, retain) NSNumber *normalListEndsAt;
#property (nullable, nonatomic, retain) NSString *updated;
#property (nullable, nonatomic, retain) NSSet<Book *> *books;
#end
#interface BookList (CoreDataGeneratedAccessors)
- (void)addBooksObject:(Book *)value;
- (void)removeBooksObject:(Book *)value;
- (void)addBooks:(NSSet<Book *> *)values;
- (void)removeBooks:(NSSet<Book *> *)values;
#end
NS_ASSUME_NONNULL_END
#import "Book.h"
NS_ASSUME_NONNULL_BEGIN
#interface Book (CoreDataProperties)
#property (nullable, nonatomic, retain) NSNumber *rankLastWeek;
#property (nullable, nonatomic, retain) NSNumber *weeksOnList;
#property (nullable, nonatomic, retain) NSString *primaryIsbn10;
#property (nullable, nonatomic, retain) NSString *primaryIsbn13;
#property (nullable, nonatomic, retain) NSString *productUrl;
#property (nullable, nonatomic, retain) NSString *bookImageUrl;
#property (nullable, nonatomic, retain) NSString *publisher;
#property (nullable, nonatomic, retain) NSString *bookDescription;
#property (nullable, nonatomic, retain) NSString *title;
#property (nullable, nonatomic, retain) NSString *contributor;
#property (nullable, nonatomic, retain) NSString *author;
#property (nullable, nonatomic, retain) NSNumber *price;
#end
NS_ASSUME_NONNULL_END

Mapping nested objects and arrays

Good afternoon, everyone!
I'm trying to figure out how to map objects with nested arrays, but my project keeps being terminated due to an uncaught exception. I assume I'm not mapping something correctly, but I'm being told something isn't key-value coding compliant.
How do I map an object with a nested array?
Following is the footprint of the JSON I'm trying to map, the interface and implementation, and the error that's being throw, respectively. Finally, there is a link to my project on GitHub, incase I've left anything out, or seeing the full source would be helpful.
JSON
{
"href": "string",
"items": [
{
"type": "string",
"status": "string",
"name": "string",
"publisher": "string",
"publisherId": "string",
"description": "string",
"url": "string",
"smallLogoImageUrl": "string",
"tileImageUrl": "string",
"heroImageUrl": "string",
"tags": [
"string",
"string"
],
"createdOn": "2015-04-22T18:55:40.782Z",
"downloadUrl": "string",
"getProductCodeUrl": "string",
"metadata": {
"exeType": "string",
"packageFileName": "string",
"installDirectory": "string",
"executableName": "string"
},
"id": "string"
}
]
}
Interface (.h)
#interface SFrontPageItem : NSObject
#property (nonatomic, strong) NSString *type;
#property (nonatomic, strong) NSString *status;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *publisher;
#property (nonatomic, strong) NSString *publisherId;
#property (nonatomic, strong) NSString *productDescription;
#property (nonatomic, strong) NSString *url;
#property (nonatomic, strong) NSString *smallLogoImageUrl;
#property (nonatomic, strong) NSString *tileImageUrl;
#property (nonatomic, strong) NSString *heroImageUrl;
#property (nonatomic, strong) NSArray *tags;
#property (nonatomic, strong) NSString *createdOn;
#property (nonatomic, strong) NSString *downloadUrl;
#property (nonatomic, strong) NSString *getProductCodeUrl;
#property (nonatomic, strong) NSArray *metadata;
#property (nonatomic, strong) NSString *productID;
#end
#interface SFrontPage : NSObject
#property (nonatomic, strong) NSString *href;
#property (nonatomic, strong) NSArray *items;
#end
Implementation (.m)
- (void) getFrontPage
{
AppDelegate *appDelegate = [[NSApplication sharedApplication] delegate];
RKObjectMapping *itemMapping = [RKObjectMapping mappingForClass:[SFrontPageItem class]];
[itemMapping addAttributeMappingsFromDictionary:#{
#"type": #"type",
#"status": #"status",
#"name": #"name",
#"publisher": #"publisher",
//#"publisherId": #"publisherId",
#"description": #"description",
#"url": #"url",
//#"smallLogoImageUrl": #"smallLogoImageUrl",
#"tileImageUrl": #"tileImageUrl",
//#"heroImageUrl": #"heroImageUrl",
//#"tags": #"tags",
#"createdOn": #"createdOn",
//#"downloadUrl": #"downloadUrl",
//#"getProductCodeUrl": #"getProductCodeUrl",
//#"metadata": #"metadata",
#"id": #"productID"
}];
//itemMapping.forceCollectionMapping = YES;
RKObjectMapping *frontpageMapping = [RKObjectMapping mappingForClass:[SFrontPage class]];
[frontpageMapping addAttributeMappingsFromDictionary:#{
#"href": #"href"
}];
[frontpageMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"items"
toKeyPath:#"items"
withMapping:itemMapping]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:frontpageMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[self.objectManager.HTTPClient setAuthorizationHeaderWithUsername:self.sconnection.apiKey.key password:self.sconnection.apiKey.secret];
[self.objectManager addResponseDescriptor:responseDescriptor];
[self.objectManager getObjectsAtPath:#"/api/frontpage/rest" parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *result)
{
SFrontPage *newFrontpage = result.firstObject;
NSLog (#" HREF: %#", newFrontpage.href);
//NSLog (#"ITEMS: %#", newFrontpage.items.firstObject);
//SFrontPageItem *newFrontpageItem = newFrontpage.items.firstObject;
//NSLog (#"Unexpected Great Thing %#", newFrontpageItem );
[appDelegate.loginViewController apiConnectionSuccess];
} failure:^(RKObjectRequestOperation *operation, NSError *error)
{
[appDelegate.loginViewController updateLoginWindowHeaderLabelTo:#"Unable to Load Frontpage"];
[appDelegate.loginViewController apiConnectionFailure];
}];
}
Error
[<SFrontPageItem 0x6180001026d0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key description.
Source
The full source can be found on GitHub, with the relevant files being APIManager.h & APIManager.m.
I hope I've been clear enough, I sometimes miss the mark when forming a question about something I don't completely understand. I'm new to both ObjC, and RestKit, so I'm sure there are already a lot of confusing things in my code. Thanks for taking the time to read through it, and consider my question. If I can clarify anything, please let me know!
Michael
You map from description to description, but the property has the identifier productDescription. Change the mapping or the property name.
Try use some ready solutions.
https://github.com/aryaxt/OCMapper
https://github.com/isair/JSONHelper

Creating Dictionary from a model that contains nil values

I have the following Model:
#interface Person : NSObject
#property (nonatomic, copy) NSString *firstName;
#property (nonatomic, copy) NSString *middleName;
#property (nonatomic, copy) NSString *lastName;
#property (nonatomic, copy) NSString *status;
#property (nonatomic, copy) NSString *favoriteMeal;
#property (nonatomic, copy) NSString *favoriteDrink;
#property (nonatomic, copy) NSString *favoriteShow;
#property (nonatomic, copy) NSString *favoriteMovie;
#property (nonatomic, copy) NSString *favoriteSport;
-(NSDictionary *)getSomeInfo;
-(NSDictionary *)getAllInfo;
#end
Part 1:
I want getSomeInfo to return NSDictionary (e.g. {"firstName", self.firstName}) for all the fields that does not contain nil. How can I do that? (I could check every value but I wonder if there's a better way)
Part 2:
I want getAllInfo to return NSDictionary with all the property and if one contains nil then it should throw an error. Again do I have to write a long conditional statement to check or is there a better way?
Note: I want to do this without using external library. I'm new to the language so I'm open to suggestions if there's a better pattern in Objective-C.
There are two approaches.
1) Check each value:
- (NSDictionary *)getSomeInfo {
NSMutableDictionary *res = [NSMutableDictionary dictionary];
if (self.firstName.length) {
res[#"firstName"] = self.firstName;
}
if (self.middleName.length) {
res[#"middleName"] = self.middleName;
}
// Repeat for all of the properties
return res;
}
2) Use KVC (Key-value coding):
- (NSDictionary *)getSomeInfo {
NSMutableDictionary *res = [NSMutableDictionary dictionary];
NSArray *properties = #[ #"firstName", #"middleName", #"lastName", ... ]; // list all of the properties
for (NSString *property in properties) {
NSString *value = [self valueForKey:property];
if (value.length) {
res[property] = value;
}
}
return res;
}
For the getAllInfo method you can do the same but instead return nil if any value is missing. Treat the nil results as your indication that not all properties have a value.

Restkit object mappings not working

I am trying to learn restkit and right now and working on object mappings. Here is the JSON that I am trying to receive and process (generated by ASP.NET MVC Web API):
[
{
"Id": 0,
"Title": "Test",
"Location": "Test",
"Score": {
"Hill": 10,
"LVille": 12
},
"TimeStart": "2012-11-10T12:30:00",
"TimeEnd": "2012-11-10T13:20:00",
"Gender": 0,
"State": 0
},
{
"Id": 0,
"Title": "ee",
"Location": "12:00",
"Score": {
"Hill": 13,
"LVille": 0
},
"TimeStart": "12:00 PM",
"TimeEnd": "12:00 PM",
"Gender": 1,
"State": 1
}
]
The objects that are being mapped to are (SBGame):
#interface SBGame : NSObject
#property (nonatomic, copy) NSNumber* Id;
#property (nonatomic, copy) NSString* Title;
#property (nonatomic, copy) NSString* Location;
#property (nonatomic, strong) SBScore* Score;
#property (nonatomic, copy) NSString* TimeStart;
#property (nonatomic, copy) NSString* TimeEnd;
#property (nonatomic, copy) NSNumber* Gender;
#property (nonatomic, copy) NSNumber* State;
#end
and SBScore :
#interface SBScore : NSObject
#property (nonatomic, copy) NSNumber* Hill;
#property (nonatomic, copy) NSNumber* LVille;
#end
and last but not least the object mappings
RKObjectMapping *ScoreMapping = [RKObjectMapping mappingForClass:[SBScore class]];
[ScoreMapping addAttributeMappingsFromArray:#[#"Hill", #"LVille"]];
RKObjectMapping *GameMapping = [RKObjectMapping mappingForClass:[SBGame class]];
[GameMapping addAttributeMappingsFromDictionary:#{
#"Id" : #"Id",
#"Title" : #"Title",
#"Location" : #"Location",
#"TimeStart" : #"TimeStart",
#"TimeEnd" : #"TimeEnd",
#"Gender" : #"Gender",
#"State" : #"State",
}];
[GameMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"score" toKeyPath:#"score" withMapping:ScoreMapping]];
RKResponseDescriptor *decp = [RKResponseDescriptor responseDescriptorWithMapping:GameMapping pathPattern:#"/Games" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:decp];
after all that the error that I recieve is
2012-12-29 16:49:19.553 Scoreboard[3004:c07] Failure with error Error Domain=org.restkit.RestKit.ErrorDomain Code=1001
"Unable to find any mappings for the given content" UserInfo=0x75c1460 {DetailedErrors=(
), NSLocalizedDescription=Unable to find any mappings for the given content, keyPath=null}
I am very new to RestKit and I have looked through The Object Mapping Tutorial but still it does not work. Any ideas are much appreciated.
I figured it out. When I created the default instance I made the base URL www.example.com/api and then just made the binding and the getObjects method to /Games
What I should have done is made the base URL www.example.com set the path pattern to /api/Games and call the getObjects method on /api/games
So from the code shown
RKResponseDescriptor *decp
= [RKResponseDescriptor responseDescriptorWithMapping:GameMapping
pathPattern:#"/Games"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
changes to
RKResponseDescriptor *decp
= [RKResponseDescriptor responseDescriptorWithMapping:GameMapping
pathPattern:#"/api/Games"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

RestKit v0.20.x: simultaneously mapping (transient) object and (core data) managed object

Suppose I want to send a request to the server with the json
{ "begin_session" : { "info" : "this is some info" } }
and I expect in response the json:
{ "token" : "this is a token", "a_objects" : [
{ "name" : "name of first a_object", "b_objects" : [
{ "name" : "name of first b_object", "type" : "some type value", "id" : "123" },
{ "name" : "name of second b_object", "type" : "some other type value", "id" : "124" }
], "id" : "id of first a_object" },
{ "name" : "name of second a_object", "b_objects" : [
{ "name" : "name of first b_object", "type" : "some type value", "id" : "123" },
{ "name" : "name of third b_object", "type" : "some third type value" , "id" : "125" },
], "id" : "id of second a_object" }
] }
I want to store "token" transiently and persist the a_objects in core data. Is this how I should do the entire process? First, I set up the objects:
#interface LoginToken : NSObject
#property (nonatomic, copy) NSString *token;
#end
#interface AObject : NSManagedObject
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSSet *bObjects;
#property (nonatomic, retain) NSString *aObjectId;
#end
#implementation AObject
#dynamic name; #dynamic bObjects; #dynamic aObjectId;
#end
#interface BObject : NSManagedObject
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) AObject *aObject;
#property (nonatomic, retain) NSString *type;
#property (nonatomic, retain) NSString *bObjectId;
#end
#implementation BObject
#dynamic name; #dynamic aObject; #dynamic type; #dynamic bObjectId;
#end
These are the request parameters:
NSDictionary *params = #{"begin_session":#{#"info":#"this is some info"}};
Then I set up the mappings:
RKObjectMapping *tokenMapping = [RKObjectMapping mappingForClass:[LoginToken class]];
[tokenMapping addAttributeMappingsFromArray:#[#"token"]];
RKResponseDescriptor *tokenResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:tokenMapping pathPattern:nil keyPath:#"token" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
RKEntityMapping *bObjectMapping = [RKEntityMapping mappingForEntityForName:#"BObject" inManagedObjectStore:objectManager.managedObjectStore];
[bObjectMapping addAttributeMappingsFromDictionary:#{#"name":#"name",#"type":#"type", #"id":#"bObjectId"}];
bObjectMapping.identificationAttributes = #[#"bObjectId"];
RKEntityMapping *aObjectMapping = [RKEntityMapping mappingForEntityForName:#"AObject" inManagedObjectStore:objectManager.managedObjectStore];
[aObjectMapping addAttributeMappingsFromDictionary:#{#"name":#"name",#"id":#"aObjectId"}];
[aObjectMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"b_objects" toKeyPath:#"bObjects" withMapping:bObjectMapping]];
aObjectMapping.identificationAttributes = #[#"aObjectId"];
Suppose objectManager is a correctly configured RKObjectManager. I set up the response descriptors:
RKResponseDescriptor *tokenResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:tokenMapping pathPattern:nil keyPath:#"token" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
RKResponseDescriptor *aObjectResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:aObjectMapping pathPattern:nil keyPath:#"a_objects" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptorsFromArray:#[tokenResponseDescriptor, aObjectResponseDescriptor]];
And finally I'll make the request:
[objectManager getObjectsAtPath:#"path" parameters:params success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
LoginToken *token = [mappingResult firstObject]; // use this token transiently
// coredata objects are auto saved
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
// handle error
}];
Is there anything I need to be aware of if this is, in fact, the correct way to do it? Also, how do I set the inverse relationship from BObject to AObject...?
As long as your CoreData file has the relationship configured properly (which it looks correct based on your object header files) then the inverse relationship is handled by the mapper. Otherwise, it should work as is.
Note that the token object will be persisted in CoreData as well so you might have to delete them that's the desired behaviour.