RestKit Identical To-One Mappings on Separate Objects Excepts - objective-c

I know what I'm doing is quite simple, and I'm a bit confused as to what I'm doing wrong here.
I'm trying to have a given key on a JSON object always map to the same Objective-C class. This key has the same name on all of the JSON object's that have it (device).
I have two classes, Foo and Bar that each have a device property:
#interface Foo
#property (strong, nonatomic) Device * device;
#end
#interface Bar
#property (strong, nonatomic) Device * device;
#end
#interface Device
#property (strong, nonatomic) NSString * deviceId;
#property (strong, nonatomic) NSString * deviceName;
#end
And for the JSON that's sent by my server for Foo and Bar, the device is represented like so:
{
"device": {
"deviceId": "someId",
"deviceName": "someName"
}
}
In a configuration function for RestKit in my application, I have the following:
RKObjectMapping * responseMapping = [RKObjectMapping mappingForClass: model[#"class"]];
if ( model[#"class"] == [Foo class] ||
model[#"class"] == [Bar class] ){
NSArray * defaultRequestKeys = #[#"id", #"createdAt", #"updatedAt"];
NSArray * defaultModelKeys = #[#"objectId", #"createdAt", #"updatedAt"];
NSArray * deviceRequestKeys = [#[#"deviceName", #"deviceId"] arrayByAddingObjectsFromArray: defaultRequestKeys];
NSArray * deviceModelKeys = [#[#"deviceName", #"deviceId"] arrayByAddingObjectsFromArray: defaultModelKeys];
RKObjectMapping * deviceResponseMapping;
deviceResponseMapping = [RKObjectMapping mappingForClass: [Device class]];
[deviceResponseMapping addAttributeMappingsFromDictionary: [NSDictionary dictionaryWithObjects: deviceModelKeys
forKeys: deviceRequestKeys]];
[responseMapping addRelationshipMappingWithSourceKeyPath:#"device" mapping: deviceResponseMapping];
}
However, RestKit excepts with failed: caught "NSInternalInconsistencyException", "Unable to add mapping for keyPath device, one already exists..."
Which means I'm doing something wrong.
I've tried looking all through the docs and I'm confused how I have the same key on two different objects use the same object mapping. This is for the Device to-one relationship on Foo and Bar.

In my case, I had declared this key already outside of the call to
[responseMapping addRelationshipMappingWithSourceKeyPath:#"device" mapping: deviceResponseMapping];
In another part of the code. Removing this key fixed the problem. I was adding it with:
[responseMapping addAttributeMappingsFromDictionary: responseAttributeMappings];

Related

NSDictionary: Comparison after MutableCopy

I have the following properties:
#property (retain, nonatomic) NSMutableDictionary * firstStartTimeObject;
#property (retain, nonatomic) NSMutableDictionary * firstLocationNameObject;
#property (retain, nonatomic) NSMutableDictionary * firstLocationAddressObject;
#property (retain, nonatomic) NSMutableDictionary * secondStartTimeObject;
#property (retain, nonatomic) NSMutableDictionary * secondLocationNameObject;
#property (retain, nonatomic) NSMutableDictionary * secondLocationAddressObject;
//This is how I make copies of the Dictionaries:
-(DataClass *)copyObjects
{
DataClass *newClass = [[DataClass alloc]init];
newClass.firstStartTimeObject = [firstStartTimeObject mutableCopy];
newClass.firstLocationAddressObject = [firstLocationAddressObject mutableCopy];
newClass.firstLocationNameObject = [firstLocationNameObject mutableCopy];
newClass.secondStartTimeObject = [secondStartTimeObject mutableCopy];
newClass.secondLocationNameObject = [secondLocationNameObject mutableCopy];
newClass.secondLocationAddressObject = [secondLocationAddressObject mutableCopy];
return newClass;
}
//In another Class I compare them
if([myClass.firstStartTimeObject isEqualToDictionary:dataClass.firstStartTimeObject])
{
[dataClass.firstStartTimeObject setValue:kCellBackGroundColor forKey:kBackGround];
}
if([myClass.firstLocationNameObject isEqualToDictionary:dataClass.firstLocationNameObject])
{
[dataClass.firstLocationNameObject setValue:kCellBackGroundColor forKey:kBackGround];
}
if([dataClass.firstLocationAddressObject isEqualToDictionary:dataClass.firstLocationAddressObject])
{
[dataClass.firstLocationAddressObject setValue:kCellBackGroundColor forKey:kBackGround];
}
if([myClass.secondStartTimeObject isEqualToDictionary:dataClass.secondStartTimeObject])
{
[dataClass.secondStartTimeObject setValue:kCellBackGroundColor forKey:kBackGround];
}
if([myClass.secondLocationNameObject isEqualToDictionary:dataClass.secondLocationNameObject])
{
[dataClass.secondLocationNameObject setValue:kCellBackGroundColor forKey:kBackGround];
}
if([myClass.secondLocationAddressObject isEqualToDictionary:dataClass.secondLocationAddressObject])
{
[dataClass.secondLocationAddressObject setValue:kCellBackGroundColor forKey:kBackGround];
}
I have Break points setup. Keys/Values in comparing dictionaries is identical, but it seems like the compiler looks at them differently as the condition is never true for it to make it inside the braces and hit the breakpoint.
I verified the keys/values via NSLog and they are identical. I even tried protocol with - (id)mutableCopyWithZone:(NSZone *)zone and got the same behavior.
Does mutableCopy of an NSMutableDicitonary change its copy to where without changing any of its contents, you compare it to the source and it's not the same? I can't figure out what I'm doing wrong!
Two dictionaries have equal contents if they each hold the same number of entries and, for a given key, the corresponding value objects in each dictionary satisfy the isEqual: test. This is how equaltodictionary work .so the issue looks like your contents is not holding the same number of entries. Please recheck your mutabledictinary object. And one more thing i have noticed is that your one if condition where you are comparing same dataclass in-spite of having one myclass.

RestKit object mapping fails depending on property name

I'm seeing some strange behavior with RestKit and mapping an object. In my destination object, if I have an NSString * property named 'description', then the entire mapping fails. Changing this to any other name, or commenting it out, everything succeeds. This happens regardless if I have a map set up for the property, and I can reproduce it in simple test classes.
The JSON the server is returning:
{
id = 1;
name = item1;
},
{
id = 2;
name = item2;
}
The object I want to map to, SimpleObject.h. Note that neither the description, nor the nonexistant property is in the JSON, meaning I don't think it's because 'description' is missing:
#import <Foundation/Foundation.h>
#interface SimpleObject : NSObject
#property (nonatomic, assign) NSInteger identifier;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *description;
#property (nonatomic, retain) NSString *nonexistant;
#end
SimpleObject.m:
#import "SimpleObject.h"
#implementation SimpleObject
#synthesize identifier;
#synthesize name;
#synthesize description;
#synthesize nonexistant;
#end
I create the mapping in my AppDelegate:
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURLString:#"http://127.0.0.1/test"];
// Setup our object mappings
RKObjectMapping *testMapping = [RKObjectMapping mappingForClass:[SimpleObject class]];
[testMapping mapKeyPath:#"id" toAttribute:#"identifier"];
[testMapping mapKeyPath:#"name" toAttribute:#"name"];
// Register our mappings with the provider using a resource path pattern
[objectManager.mappingProvider setObjectMapping:testMapping forResourcePathPattern:#"/products"];
Inside my didLoadObjects method, I have:
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects
{
NSLog(#"Loaded products: %#", objects);
SimpleObject *obj = [objects objectAtIndex:0];
NSLog(#"Loaded product ID %d -> Name: %# ", obj.identifier, obj.name);
}
This outputs:
Finished performing object mapping. Results: {
"" = (
(null),
(null)
);
}
If I comment out the description property (but leave in nonexistant), then everything works:
Finished performing object mapping. Results: {
"" = (
"<SimpleObject: 0x8a4d040>",
"<SimpleObject: 0x8a50130>"
);
}
Is there a reason the mapping is failing only for this property name? If I change the name to anything other than 'description', it also succeeds.
It is conflicting with description property of NSObject. Rename it to other(like desc).

RestKit Object Mapping did not mapping my object

{"avatar_30":"url",
"id": 81,
"name": "\u674e\u5f3a"
}
I have my Profile Class.
#interface Profile: NSObject{
NSNumber* _identifier;
NSString* _name;
NSString* _avatar_30;
}
#property (nonatomic, retain) NSNumber* identifier;
#property (nonatomic, retain) NSString* name;
#property (nonatomic, retain) NSString* avatar_30;
#end
#implementation Profile
#synthesize identifier=_identifier;
#synthesize name=_name;
#synthesize avatar_30 = _avatar_30;
And then I build my manager and get mapping for Profile Class like these:
in AppDelegate.h
#interface AppDelegate : UIResponder
in AppDelegate.m
RKObjectMapping* profileMapping = [RKObjectMapping mappingForClass:[Profile class]];
[profileMapping mapKeyPath:#"id" toAttribute:#"identifier"];
[profileMapping mapKeyPath:#"name" toAttribute:#"name"];
[profileMapping mapKeyPath:#"avatar_30" toAttribute:#"avatar_30"];
RKObjectManager* manager = [RKObjectManager objectManagerWithBaseURL:_baseURL];
//[manager.mappingProvider setMapping:profileMapping forKeyPath:#"%#"];
[manager loadObjectsAtResourcePath:#"/api/v2/userinfo/100/" objectMapping:profileMapping delegate:self];
I can get the jason correctly, however the profile can not be mapped.
Got a JSON response back from GET!
2012-04-10 17:47:33.480 Base_01[5714:14603] D restkit.network:RKObjectLoader.m:198 Found directly configured object mapping, creating temporary mapping provider for keyPath '%#'
2012-04-10 17:47:33.482 Base_01[5714:fb03] Load collection of Profiles: (
)
Could any have any idea for this problem ? Thanks for your help!
In the method
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects{
are there any objects in the "objects" array?
This should contain all your Profile objects.

Saving to database using FMDB crashes on interpreting NSInteger

When the below function is called, I get the EXC_BAD_ACCESS crash. It looks like FMDB is having a problem interpreting the subject_id NSInteger as it makes it through the two NStrings and bombs when it hits this subject_id column in the WHERE clause.
- (void) saveAllData {
if(isDirty) {
DrillDownAppAppDelegate *appDelegate = (DrillDownAppAppDelegate *)[[UIApplication sharedApplication] delegate];
FMDatabase *database = [FMDatabase databaseWithPath:appDelegate.getDBPath];
if ([database open]) {
[database executeUpdate:#"update Subject Set subject = ?, category = ? where subject_id = ?",
self.title, self.category_title, self.subject_id];
[database close];
}
isDirty = NO;
}
//Reclaim all memory here.
[title release];
title = nil;
[category_title release];
category_title = nil;
}
The problem is the same as I ran into in another post on FMDB insert problems and this boils down to something wrong with my subject_id member. I believe I am using a wrong declaration in the header. Here it is:
//
// Subject.h
// DrillDownApp
#import <UIKit/UIKit.h>
#interface Subject : NSObject {
NSInteger subject_id;
NSString *category_title;
NSString *title;
// NSMutableArray *quotes;
BOOL isDirty;
// BOOL isDetailViewHydrated;
}
- (id) initWithPrimaryKey:(NSInteger)pk;
#property (nonatomic, readwrite) BOOL isDirty;
//#property (nonatomic, readwrite) BOOL isDetailViewHydrated;
- (void) addSubject;
- (NSInteger)getNextSubjectId;
#property (nonatomic, assign) NSInteger subject_id;
#property (nonatomic, copy) NSString * title;
#property (nonatomic, copy) NSString * category_title;
//#property (nonatomic, retain) NSMutableArray *quotes;
//- (void) saveAllData;
#end
(Note: I edited this majorly as I figured out the rest of it.)
Ok I solved it. FMDB will not work using Integers. You must convert them into Numbers. I found this done in the examples on FMDB doc and there is never an int being passed through an executeUpdate statement.
So in my example above the way I fixed this was with the following:
[database executeUpdate:#"update Subject Set subject = ?, category = ? where subject_id = ?", self.title, self.category_title, [NSNumber numberWithInt:self.subject_id]];
I wished this was better documented, but oh well.
I don't know from where you are calling the method saveAllData. Still your question lacks requirement so anyone to answer.
Also, I have found one problem with your code.
Instead of the following code,
//Update the value.
[sub setTitle:txtSubject.text];
[sub setCategory_title:txtCategory.text];
use the following code
//Update the value.
sub.title = txtSubject.text;
sub.category_title = txtCategory.text;
so that the strings have the copy property. If you override the setter method, then you might need to explicitly copy the string. Else it will just assign the value without copy.

Mapping nested objects with RestKit in Xcode using Objective-C

With the help of mja, I managed to successfully set up simple object mapping using RestKit and Objective-C. Please see my previous question here.
My next step was to attempt to deal with nested JSON in the same way.
My JSON looks like this, with the outer being a CandidatePhrase with some nested 'Votes':
{ "Id":33696,
"Phrase": "phrase",
"BadCount":0,
"Votes":[{"Id":447,"OriginalId":33696,"Votes":2,"Translation":"translation 1"},
{"Id":746,"OriginalId":33696,"Votes":1,"Translation":"translation 2"},
{"Id":747,"OriginalId":33696,"Votes":1,"Translation":"translation 3"}
]}
I created a relationship in my AppDelegate as follows:
[candidatePhraseMapping mapKeyPath:#"votes" toRelationship:#"vote" withMapping:voteMapping];
When I call make my request in my controller, I'm able to deal with the rest of the CandidatePhrase okay, but am not really sure how to map the nested 'Vote' objects into an array so I can use them in a TableView
(pseudo-code something like this...)
// Store the votes in an array
_votes = [[NSArray alloc] initWithObjects:myCandidatePhrase.votes, nil];
Here's my CandidatePhrase Object
#interface CandidatePhrase : NSObject
#property (nonatomic, retain) NSNumber* ident;
#property (nonatomic, retain) NSNumber* badcount;
#property (nonatomic, retain) NSString* phrase;
#property (nonatomic, retain) NSArray* votes;
#end
and my Vote object
#interface Vote : NSObject
#property (nonatomic, retain) NSNumber* ident;
#property (nonatomic, retain) NSNumber* originalId;
#property (nonatomic, retain) NSNumber* votecount;
#property (nonatomic, retain) NSString* translation;
+ (id)voteWithTranslationId:(NSNumber *)ident translation:(NSString *)translation;
#end
Any help would be much appreciated.
EDIT
Below is my mapping code
// Votes Mapping
RKObjectMapping* voteMapping = [RKObjectMapping mappingForClass:[Vote class]];
[voteMapping mapKeyPath:#"Id" toAttribute:#"ident"];
[voteMapping mapKeyPath:#"OriginalId" toAttribute:#"originalId"];
[voteMapping mapKeyPath:#"Votes" toAttribute:#"votecount"];
[voteMapping mapKeyPath:#"Translation" toAttribute:#"translation"];
[[manager mappingProvider] addObjectMapping:voteMapping];
// Candidate Phrase Mapping
RKObjectMapping *candidatePhraseMapping = [RKObjectMapping mappingForClass:[CandidatePhrase class]];
[candidatePhraseMapping mapKeyPath:#"Id" toAttribute:#"ident"];
[candidatePhraseMapping mapKeyPath:#"Phrase" toAttribute:#"phrase"];
[candidatePhraseMapping mapKeyPath:#"BadCount" toAttribute:#"badcount"];
[candidatePhraseMapping mapKeyPath:#"Votes" toRelationship:#"votes" withMapping:voteMapping];
[[manager mappingProvider] addObjectMapping:candidatePhraseMapping];
For clarity also, here's how I'm attempting to access the vote items on the controller
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObject:(id)object
{
CandidatePhrase *myCandidatePhrase = (CandidatePhrase*)object;
self.candidateText.text = myCandidatePhrase.phrase; <-- works fine
_votes = [[NSArray alloc] initWithObjects:myCandidatePhrase.votes, nil];
for (id o2 in _votes) {
//Vote *vote = o2;
NSLog(#"Item name: %#", o2); <-- sees object but crashes
}
NSLog(#"Votes: %#", myCandidatePhrase.votes);
_votes = [[NSArray alloc] initWithObjects:myCandidatePhrase.votes, nil];
[_votesTableView reloadData];
}
and my table is binding with
Vote *vote = [_votes objectAtIndex:indexPath.row];
cell.textLabel.text = vote.translation;
You do not need to manually manage the nested NSArray. I believe the problem might be just a simple typo, as you map keyPath "votes", but your json contain "Votes" with capital "V".
[candidatePhraseMapping mapKeyPath:#"Votes" toRelationship:#"votes" withMapping:voteMapping];
If this doesn't help feel free to leave a comment and update your question with voteMapping.
Also, the contents of the didLoadObject can be simplified:
//in .h file
#property (nonatomic, retain) NSArray* votes;
// in implementation
#synthesize votes;
...
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObject:(id)object
{
CandidatePhrase *myCandidatePhrase = (CandidatePhrase*)object;
self.candidateText.text = myCandidatePhrase.phrase;
self.votes = myCandidatePhrase.votes;
}