RestKit direct + inverse relationship mapping - core-data

As a newbie RestKit user I'm having a conceptual understanding problem with CoreData relationship mapping.
Let's say we have a CoreData model with just two entities in master/detail relationship:
#interface Master : NSManagedObject
#property (nonatomic, retain) NSString *objectId;
#property (nonatomic, retain) NSString *display;
#property (nonatomic, retain) NSSet *childrens;
#end
#interface Children : NSManagedObject
#property (nonatomic, retain) NSString *objectId;
#property (nonatomic, retain) NSString *display;
#property (nonatomic, retain) Master *father;
#end
The RestKit mapping for this model is:
RKEntityMapping *masterMapping = ...
RKEntityMapping *childrenMapping = ...
... property mappings ...
masterMapping.identificationAttributes = #[ #"objectId" ];
childrenMapping.identificationAttributes = #[ #"objectId" ];
[masterMapping addPropertyMapping:[RKRelationshipMapping
relationshipMappingFromKeyPath:#"childrens" toKeyPath:#"childrens"
withMapping:childrensMapping]];
[childrenMapping addPropertyMapping:[RKRelationshipMapping
relationshipMappingFromKeyPath:#"father" toKeyPath:#"father"
withMapping:masterMapping]];
Here is how the http get response is modeled (I can change it):
{
objectId: "3",
display: "a master object",
childrens: [
{
objectId: "1",
display: "a child object",
father: { objectId: "3" }
},
{
objectId: "2",
display: "another child object",
father: { objectId: "3" }
}
]
}
The problem is that the previous mapping definition would lead to a circular mapping error from RestKit when associating it with some RKResponseDescriptor.
I've read RestKit documentation and many stackoverflow.com similar threads, but still I don't understand how to set up a full CoreData model relationship mapping, provided that I need to have both relations available in my code (i.e. I need to explicitly access father from a children, and childrens from a master entity).
Any help will be appreciated.
Many thanks in advance!

I've figured out by myself my mapping understanding problem, and I'd like to share that just in case: a RestKit model mapping is not meant to be defined on the model in "absolute" terms, but "relatively" to the RESTful operations on the model.
Put in other terms, a model entity can have more than one RestKit mapping, each corresponding to how that entity is referred in GET/POST/PATCH/etc. operations.
I hope this can help others to ease the understanding of RestKit beautiful framework!

Related

Is there way to check device model in Apportable SDK?

I have to implement some special behaviour for particular device model. So I need to check device model.
I'm using
[UIDevice currentDevice] name];
but it's return me something like this
Nexus 7 running Apportable
I think it's kind of weird result. Any other way to do it?
use [[UIDevice currentDevice] nativeModel]
NSLog(#"%#", [[UIDevice currentDevice] nativeModel]);
will generate D/Spin ( 3956): 2014-04-14 09:44:21.446 Spin[3956:2752] Nexus 7 in logcat output
There is more information about the UIDevice API extensions in .apportable/SDK/System/UIKit/UIDevice.h
Apportable typically makes API decisions for iOS ports to be as seamless as possible. If you actually want an Android specific result, a non-iOS API extension is usually created.
Here are some details on the UIDevice.h mappings to the Android Build API's:
#property (nonatomic, readonly) NSString *nativeSystemName; -> #"Android"
#property (nonatomic, readonly) NSString *nativeCPUABI; -> Build.CPU_ABI
#property (nonatomic, readonly) NSString *nativeModel; -> Build.MODEL
#property (nonatomic, readonly) NSString *nativeProduct; -> Build.PRODUCT
#property (nonatomic, readonly) NSString *nativeManufacturer; -> Build.MANUFACTURER
#property (nonatomic, readonly) NSString *nativeSystemVersion; -> Build.RELEASE
#property (nonatomic, readonly) NSUInteger nativeSDKVersion; -> Build.SDK_INT

CoreData: error: Failed to call designated initializer on NSManagedObject class 'Collect'

In order to wrap some logic to model, I plan to put NSManagedObjectContext in one NSManagedObject model, so I can handle many common logic in one model:
#interface Collect : NSManagedObject{
NSManagedObjectContext *managedObjectContext;
}
#property (nonatomic, retain) NSNumber *created_at;
#property (nonatomic, retain) NSString *name;
- (void) initContext;
#end
#implementation Collect
#dynamic created_at;
#dynamic name;
- (void) initContext{
if (managedObjectContext == nil)
{
managedObjectContext = [MyAppDelegate managedObjectContext];
}
}
#end
And I use it by:
Collect *collect = [[Collect new] autorelease];
[collect initContext];
But when run it shows:CoreData: error: Failed to call designated initializer on NSManagedObject class 'Collect'.
I want to know How to add NSManagedObjectContext instance to NSManagedObject model correctly ?
You cannot add a managed object context to an managed object, it works the other way around: You need a managed object context first, and then you can create objects in this context.
See e.g. the NSManagedObjectContext documentation:
An instance of NSManagedObjectContext represents a single “object
space” or scratch pad in an application. Its primary responsibility is
to manage a collection of managed objects. These objects form a group
of related model objects that represent an internally consistent view
of one or more persistent stores. A single managed object instance exists in one and only one context, but multiple copies of an object
can exist in different contexts.
and the NSManagedObject documentation:
If you instantiate a managed object directly, you must call the designated initializer
(initWithEntity:insertIntoManagedObjectContext:).
There is also an utility method insertNewObjectForEntityForName:inManagedObjectContext: to create new objects. For example:
Collect *collect = [NSEntityDescription insertNewObjectForEntityForName:#"Collect"
inManagedObjectContext:[MyAppDelegate managedObjectContext]];
(You could put that into a class method of Collect if you like.)
And note that NSManagedObject already has a managedObjectContext method to get the context of the object:
NSManagedObjectContect *context = [collect managedObjectContext];
It is therefore not necessary (or might even cause problems) if you add an instance variable managedObjectContext to your managed object class.

Transient attribute not found in entity

I'm trying to get the first letter of an attribute (autor) from my pre populated sqlite database. I have no problems retriving data from the database using coredata. But when I try to get data from my transient property, I get this error message: "NSFetchedResultsController ERROR: object { Autor = "\U00cdtalo Calvino"; } returned nil value for section name key path 'FirstLetter'. Object will be placed in unnamed section"...
I have created a transient attribute called FirstLetter and inside my entity Cit. I have also my class for the entity defined.
Cit.h
#interface Cit : NSManagedObject {
#private
}
#property (nonatomic, retain) NSString * Autor;
#property (nonatomic, retain) NSString * FirstLetter;
- (NSString *) FirstLetter;
#end
Cit.m
#import "Cit.h"
#implementation Cit
#dynamic Autor;
#dynamic FirstLetter;
- (NSString *) FirstLetter {
NSLog(#"doing");
[self willAccessValueForKey:#"FirstLetter"];
NSString * initial = [[self valueForKey:#"Autor"] substringToIndex:1];
[self didAccessValueForKey:#"FirstLetter"];
return initial;
}
#end
I cannot get the it to work. Does anyone have a solution for that??? thanks!
I did something similar in my model class, but I didn't bother creating a transient object in the model. I just added a - (NSString *)sectionIndex method to my model that returned the first letter of the string, same as you.
So, I'd try deleting the transient attribute from your model and see if that works any better.

How to create generic id type properties?

I'm new to iOS and Objective-C and trying to create a class that has a generic property.
#interface HeaderInfo : NSObject {
NSString *label;
id value;
}
#property (nonatomic, retain) NSString *label;
#property (nonatomic, retain) id value;
- (HeaderInfo *)initWithLabel:(NSString *)lbl value:(id)val;
#end
Then I'm trying to add this class to an array:
[generalSection.items addObject:[[HeaderInfo alloc] initWithLabel:#"Tacho seal security" value:#"Some String Value"]];
[generalSection.items addObject:[[HeaderInfo alloc] initWithLabel:#"Tacho seal security" value:YES]];
but compiler doesn't like the 2nd addition and says:
Warning: passing argument 2 of 'initWithLabel:value:' makes pointer from integer without a cast
What I'm doing wrong? Any help is greatly appreciated.
And also how can check the value later on whether it's a BOOL or NSString?
Thanks.
The BOOL type is not a object, so you can't pass it as id (a generic object).
You should pass an NSNumber. An NSNumber is an object that encapsulate numbers when you want to pass a number as an object.
You can create a NSNumber like this : [NSNumber numberWithBool:YES];
You can retrieve the value with [value boolValue];
If you want to check the type of object you're having at runtime you can to it like that :
if ([value isKindOfClass:[NSNumber class]]) {
//It's a number
}
else if ([value isKindOfClass:[NSString class]]) {
//It's a string
}

Core Data question

the problem I am trying to solve in an application that is using Core Data is to be able to hold a calculated value in a NSManagedObject custom ivar. The calculated value that I want to store is in fact an image. I do not want to persist these images; I build them and destroy them throughout the lifetime of the application. I tried along the lines of:
#interface RTStaffImage : NSManagedObject {
UIImage *image;
}
// Custom properties
#property (nonatomic, retain) UIImage *image;
// Managed object properties
#property (nonatomic, retain) NSNumber *imageID;
#property (nonatomic, retain) NSString *imageName;
and custom accessors methods:
- (void)setImage (UIImage*)im;
- (UIImage *)image;
and in the implementation:
#implementation RTStaffImage
#synthesize image;
#dynamic imageID;
#dynamic imageName;
This fails at runtime with unrecognised selector problems:
-[NSManagedObject setImage:]: unrecognized selector sent to instance
The above approach is what Apple (or, at least as far as I see having read the docs) outlines for transient properties so it should work :-(
Any ideas, comments?
- (void)setImage (UIImage*)im;
you are missing a colon between setImage and (UIImage*). This is the correct version:
- (void)setImage:(UIImage*)im;
And where are the implementations of those two methods?
-[NSManagedObject setImage:]: unrecognized selector sent to instance
just curious, I read NSManagedObject there, are you sure you create an instance of RTStaffImage there?
Yeah, you have these backwards:
#synthesize image;
#dynamic imageID;
#dynamic imageName;
You're providing an implementation for setImage and image, so image should be #dynamic, and the others you need synthesized methods for, so use #synthesize for imageID and imageName.
Good point, they should all be #dynamic since you're with CoreData.
2nd attempt: you have set RTStaffImage as the Class name in the Entity, right?
3rd attempt: is RTStaffImage.m actually part of the Target being built?

Resources