RestKit Object Mapping relationships with foreign keys - core-data

Can RestKit connect a relationship without storing the foreign key as an attribute, i.e., directly from the keypath in the JSON?
In particular, I've got a Job has_many Rooms relationship. The room's JSON doesn't contain the job, rather, both are loaded separately:
- job: {
id: 1,
name: "John"
}
- room: {
id: 4,
job_id: 1,
name: "spare bedroom"
}
The Job is loaded before the Room.
My CoreData models, Job has properties for
#interface Job : NSManagedObject
#property (nonatomic, retain) NSNumber * identifier;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSSet *rooms;
#end
#interface Room : NSManagedObject
#property (nonatomic, retain) NSNumber * identifier;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) Job *job;
#end
Currently I add a #property (nonatomic, strong) NSNumber *jobID; to Room, which I #synthesize.
JobMapping:
mapping = [RKManagedObjectMapping mappingForClass:[Job class]];
[mapping setPrimaryKeyAttribute:#"identifier"];
[mapping mapAttributes:#"name", nil];
[mapping mapKeyPath:#"id" toAttribute:#"identifier"];
[mapping mapRelationship:#"rooms" withMapping:[Room objectMapping]];
RoomMapping
mapping = [RKManagedObjectMapping mappingForClass:[Room class]];
[mapping setPrimaryKeyAttribute:#"identifier"];
[mapping mapAttributes:#"name", nil];
[mapping mapKeyPath:#"id" toAttribute:#"identifier"];
[mapping mapKeyPath:#"job_id" toAttribute:#"jobID"];
[mapping mapRelationship:#"job" withMapping:[Job objectMapping]];
[mapping connectRelationship:#"job" withObjectForPrimaryKeyAttribute:#"jobID"];
I was wondering if there's a way I can do this without the extra jobID property? I don't want to have a jobID attribute in the CoreData xcdatamodeld - it's redundant, as the relationship covers that.
Also if I rebuild the NSManagedObjects, I need to re-add the jobID property, which is tedious. Can't I tell restkit to connect the Room to its corresponding Job via the job_id keypath in the JSON?
If I remove the property, the mapKeyPath:#"job_id" line, and change the last line to [mapping connectRelationship:#"job" withObjectForPrimaryKeyAttribute:#"job_id"]; I get
the entity Room is not key value coding-compliant for the key "job_id".

I would make JobId a transient value in core data, and write a custom set and get for it.
The set would set the relationship to self.job=methodToReturnObjectMatchingJobId (this would be used by rest kit)
The get would return self.job.identifier
If you are not using mogenerator, I would suggest you have a look at it for all your core data needs.
Below is sample code of how i did it:
-(void) setClaimId:(NSNumber *)claimId{
Claim *propertyClaim=[Claim listItemFromId:[claimId intValue] withContext:self.managedObjectContext];
self.claim=propertyClaim;
}
-(NSNumber*) claimId{
return self.claim.referenceId;
}
where listItemFromId is a simple query that returns the object based on the id.

Related

Problems accessing 1:N relationship in entity

https://github.com/lokming/QuestionBank
I have entities: Bank,Section,Subsection,Set,Question.
I am having problems accessing relationship "NSSet Subsection" in entity Section and getting the message "[UITableViewCell thesubsection]: unrecognized selector sent to instance" from this code
in CRSectionVC.m which fills a tableview cell
- (NSArray *)allQuestions
{
NSSortDescriptor *division = [NSSortDescriptor sortDescriptorWithKey:#"subdivision" ascending:YES];
return [_section2.thesubsection sortedArrayUsingDescriptors:#[division]];
}
I can however access the "NNSet section" relationship in Bank entity using this code
NSSortDescriptor *division2 = [NSSortDescriptor sortDescriptorWithKey:#"division" ascending:YES];
return [self.detailItem2.thesection sortedArrayUsingDescriptors:#[division2]];
_section2 is declared in CRSubsectionVC.h
#property (strong, nonatomic) Section *section2;
The storyboard is
1. CRMasterViewController which displays ‘category’ attribute from Bank entity into a tableview,
Bank.h
#class Section;
#interface Bank : NSManagedObject
#property (nonatomic, retain) NSString * category;
#property (nonatomic, retain) NSSet *thesection;
#end
Bank.m
#implementation Bank
#dynamic category;
#dynamic thesection;
#end
When I tap a ‘category’ I segue and pass a Bank object to CRDetailViewController. I use following code:
NSSortDescriptor *division2 = [NSSortDescriptor sortDescriptorWithKey:#"division" ascending:YES];
return [self.detailItem2.thesection sortedArrayUsingDescriptors:#[division2]];
to fetch section relationship (NSSet *thesection) ‘division’ attribute from Bank into tableview.
Section.h
#class Bank, Subsection;
#interface Section : NSManagedObject
#property (nonatomic, retain) NSString * division;
#property (nonatomic, retain) Bank *bank;
#property (nonatomic, retain) NSSet *thesubsection;
#end
Section.m
#implementation Section
#dynamic division;
#dynamic bank;
#dynamic thesubsection;
#end
If I tap a ‘section’ ’ I segue and pass a Section object to CRSubsectionVC named _section2. When I try to access NSSet *thesubsection to get ‘subdivision’ attribute using code
NSSortDescriptor *division = [NSSortDescriptor sortDescriptorWithKey:#"subdivision"ascending:YES];
return [_section2.thesubsection sortedArrayUsingDescriptors:#[division]];
I get the error [UITableViewCell thesubsection]: unrecognized selector sent to instance. I cannot figure out why automated accessor ‘thesection’ works OK but not ‘thesubsection’ .
Subsection.h
#class Section, Set;
#interface Subsection : NSManagedObject
#property (nonatomic, retain) NSString * subdivision;
#property (nonatomic, retain) Section *section2;
#property (nonatomic, retain) NSSet *set;
#end
Subsection.m
#implementation Subsection
#dynamic subdivision;
#dynamic section2;
#dynamic set;
#end
Fixed the problem. A case of not copying code exactly from working template and understanding core data and table views.

In Core Data, how to get all object of NSSet?

I currently have the following Core Data Model:
Category
#interface Category : NSManagedObject
#property (nonatomic, retain) NSSet *subcategory;
Subcategory
#interface SubCategory : NSManagedObject
#property (nonatomic, retain) Category *category;
#property (nonatomic, retain) NSSet *items;
Items
#interface Item : NSManagedObject
#property (nonatomic, retain) SubCategory *subCategory;
I want to get all items in a category, however when I write:
NSLog(#"Items in Category:%#", self.category.subcategory.items)
I get nothing.
What would be the way to fetch the information? Since I am receiving the class with a property I added in the ViewController:
#property (strong, nonatomic) Category *category;
One possible solution is to use the Key-Value Coding
Collection Operator
#distinctUnionOfSets:
NSSet *allItems = [self.category valueForKeyPath:#"subcategory.#distinctUnionOfSets.items"];
(Note that a better name for the to-many relationship "subcategory" would be
"subcategories".)
Alternatively, you can execute a fetch request on the "Item" entity with a predicate
using the inverse relationships:
[NSPredicate predicateWithFormat:#"subCategory.category == %#", self.category];
The advantage of this method is that the intermediate "SubCategory" objects are
not loaded into memory.

How to make predicate to Entity's customized property

This question is an ongoing one for my previous question.
I want to make a predicate to filter the entity by my customized property, this is the entity class
#interface Expense : NSManagedObject
#property (nonatomic, retain) NSDate * date;
#property (nonatomic, retain) NSNumber * amount;
#property (nonatomic, retain) NSString * category;
#property (nonatomic, retain) NSString * note;
#property (nonatomic, retain) NSString * paidby;
//customized property
#property (readonly) int year;
#property (readonly) int month;
#property (readonly) NSString *yyyy_mmm;
#property (readonly) NSString *mm_yyyy;
#end
And this is my predicate:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"yyyy_mmm == %#", header];
When run:
error message NSInvalidArgumentException', reason: 'keypath yyyy_mmm
not found in entity
This is not completely unexpected, I searched and find out it has something to do with key-value, listener stuff. But I do not know what to do exactly.
Any help will be so appreciated, and BTW the coding block has some problem, please forgive me.
Make any property you need to use in the predicate an actual key on the entity in your data model. If you can set the value directly, great, do that. If not you either want to implement willSave or use KVO and update your predicate key values from the other values in your entity.

Implementing a Transient Property with Core Data

I can't figure this out. What I have read about transient properties tells me they can be identified in the object model with an undefined type. But the compiler complains about this with an error that the type is unknown.
From the Core Dat Programming Guide:
If the non-supported attribute is an object, then in the managed object model you specify its type as undefined, and that it is transient. When you implement the entity’s custom class, there is no need to add an instance variable for the attribute—you can use the managed object's private internal store. A point to note about the implementations described below is that they cache the transient value. This makes accessing the value more efficient—it is also necessary for change management. If you define custom instance variables, you should clean up these variables in didTurnIntoFault rather than dealloc or finalize.
Here is the header file:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class SearchTerms;
#interface SearchResult : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString * lattitude;
#property (nonatomic, retain) NSString * details;
#property (nonatomic, retain) NSString * endTime;
#property (nonatomic, retain) NSString * longitude;
#property (nonatomic, retain) NSString * city;
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) NSString * imageLink;
#property (nonatomic, retain) NSString * startTime;
#property (nonatomic, retain) UNKNOWN_TYPE coordinate;
#property (nonatomic, retain) UNKNOWN_TYPE subtitle;
#property (nonatomic, retain) SearchTerms * searchUsed;
#end
I am trying to include the properties for an MKAnnotation with title, subtitle, and coordinate . Here, I need to derive subtitle from other fields, and derive coordinate from longitude and latitude.
I'm not sure how to reconcile what the Guide says and what looks plainly wrong, and the compiler says so.
Once I get the header right, I may be able to get the implementation right, and I'll use awakeFromFault to set the values. I'm not sure if I need to release the subtitle, which will be an NSString, using didTurnIntoFault, but that seems to be what the Guide says to do.
I haven't seen really good example of how to implement a simple transient property. I am tempted to jsut add the properties to the managed object entity and forget about mentioning it in the managed object model. But it seems that I would be overlooking something if I do that.
You need to change the property's type to id, or whatever is most suitable:
#interface SearchResult : NSManagedObject
{}
#property (nonatomic, retain) id coordinate;
#end
Another way to handle this is via KVC and dependent keys:
#implementation SearchResult
+ (NSSet *) keyPathsForValuesAffectingCoordinate
{
return [NSSet setWithObjects:#"latitude", #"longitude", nil];
}
- (id) coordinate
{
// Derive the coordinate value
}
#end
The problem here is that the MKAnnotation protocol stores its coordinate value in a CLLocationCoordinate2D which, is not an object but a struct and it does not support key-value coding. To use it as a transitional property, you will need to wrap it in an object.
The Core Data Programming Guide: Scalar Value Constraints
If you want to use a scalar type or structure that is not one of those
supported directly by Core Data and not one of the structures
supported by key-value coding, you must store it in your managed
object as an object—typically an NSValue instance, although you can
also define your own custom class. You will then treat it as an object
value as described later in this article. It is up to users of the
object to extract the required structure from the NSValue (or custom)
object when retrieving the value, and to transform a structure into an
NSValue (or custom) object when setting the value.

After adding objects via CoreDataGeneratedAccessors to-many relationships's NSSet is empty

I have two Core Data entities: Parent and Child. Parent has to-many relationship to a Child called children. Inverse relationship from is Child.parent. So parent has CoreDataGeneratedAccessors: - (void)addChildrenObject:(Child *)value; and - (void)addChildren:(NSSet *)value;.
Problem: after I add Child(s) by using one of those accessors and save managedObjectContext parent.children is empty. At the same time parent property of every added Child point to proper instance of Parent and NSFetchedResultsController fetches such children (predicate is parent = %#, <instance of Parent>) well.
How can it be so? Just don't have a clue how to debug such a strange CoreData behavior.
Solved. Somehow property of that set was sythesized by #synthesize not #dynamic in .m file. I know this was a very stupid typo, but I wonder why XCode did not even generated a warning about that! Static analyzer said nothing about it too!
The same happens when you define properties like this:
#interface Project : BaseModel {
Workspace *workspace;
NSString *name;
}
#property (nonatomic, retain) Workspace *workspace;
#property (nonatomic, retain) NSString *name;
Correct interface should look like this:
#interface Project : BaseModel {}
#property (nonatomic, retain) Workspace *workspace;
#property (nonatomic, retain) NSString *name;

Resources