Binding to a relations property in Core Data - objective-c

I'm new in Core Data, and i got a problem i can't get my head around how to do "the right way"
I'll try and examplify my problem.
I got a entity Car. And a list of all the cars in my program. The cars have some attributes, but they are not predefined. So for each car i want to be able to define some properties.
Therefore i have defined a new entity CarProperty, with a one to many relation with the car.
In the nscollectionview i would like to show some of the properties from the car, more specefic the number of kilometer (numKm) it has driven (if that property exist). So i want to bind it to a label. But how to do?
I can't say representedObject.properties.numKm, or representedObject.numKm.
How should I get around this?
Hope it makes sense.

This isn't an easy problem. The thing is, Core Data doesn't know anything about numKm as a property. How is it supposed to know that numKm corresponds to a particular CarProperty object?
The fundamental problem you're describing is key-value coding compliance. Cocoa's going to look for a method called numKm on the properties object. Not finding one, it'll try sending [properties valueForKey:#"numKm"]; Since valueForKey: doesn't know what to do with numKm, you get an error, but not before it calls [properties valueForUndefinedKey:#"numKm"]
But here's the catch: properties is an NSSet generated by Core Data, so you can't subclass it to override valueForUndefinedKey:. What you can do is create your own object that's KVC-compliant for your arbitrary properties and use that instead.
One solution is to subclass NSDictionary and make it act as a proxy. The primitive methods are count, objectForKey: and keyEnumerator. If you override these three methods, you can create an NSDictionary that's linked to your Car object and returns the appropriate CarProperty objects. For example:
#interface PropertyProxy : NSDictionary {
}
#property (nonatomic, readonly, assign) Car *car;
- (id)initWithCar:(Car *)car
#end
#implementation PropertyProxy
#synthesize car = _car;
- (id)initWithCar:(Car *)car {
if (!(self = [super init]))
return nil;
_car = car;
return self;
}
- (NSInteger)count {
return [car.properties count];
}
- (id)objectForKey:(NSString *)key {
return [[car.properties filteredSetUsingPredicate:[NSPredicate predicateWithFormt:#"key == %#", key]] anyObject];
}
- (NSEnumerator *)keyEnumerator {
return [[car valueForKeyPath:#"properties.key"] objectEnumerator];
}
#end
Then, in your Car class, do this:
#interface Car : NSManagedObject {
// other stuff
}
#property (nonatomic, readonly) NSDictionary *carProperties;
// other stuff
#end
#implementation Car
// other stuff
- (NSDictionary *)carProperties {
return [[[PropertyProxy alloc] initWithCar:self] autorelease];
}
#end
(Disclaimer: I just typed this into my web browser, so no guarantees this actually compiles :-))
As you can see, it's not the easiest thing in the world to do. You'll be able to set up key paths like this:
representedObject.carProperties.numKm;
Keep in mind that, while this is key-value coding compliant, it is not key-value observing compliant. So if numKm changes, you won't be able to observe that. You would need to do some extra work to make that happen.

Related

How is dictionary[#"key"] implemented in objective c?

If we want to override [dictionary objectForKey:#"key"] it can be done by subclassing NSDictionary class. How to override dictionary[#"key"]? In this context I want to know how dictionary[#"key"] is implemented.
Thanks!
Edit:
I wanted to find a scalable way to parse an API response while preventing [NSNull null] from crashing my app. I have written category for NSDictionary, but I wanted a way to parse in this syntax: data[#"key"]
So, I was evaluating the feasibility of subclassing NSDictionary.
When you use dictionary[#"key"] this gets converted into a call to [dictionary objectForKeyedSubscript:#"key"]. objectForKeyedSubscript: has the same behaviour as objectForKey:.
If you want to change the behaviour of dictionary[#"key"] then you will need to override objectForKeyedSubscript:.
Apple's NSDictionary API Reference has a little more information.
EDIT: This question is pretty old now, but it just recently came to my attention again, so I thought I would add an example of wrapping NSDictionary as I would recommend. This could be made more robust and feature rich, but it's an okay place to start.
#interface NilSafeDictionary: NSObject
-(instancetype)initWithDictionary:(NSDictionary*)dictionary;
#end
/** Category on NSDictionary to create a nil-safe wrapper. */
#interface NSDictionary <NilSafeConvenience>
- (NilSafeDictionary*)nilSafe;
#end
#implementation NilSafeDictionary {
NSDictionary* _internalDict;
}
- (instancetype)initWithDictionary:(NSDictionary*)dictionary {
self = [super init];
if (self) {
_internalDict = [dict copy];
}
return self;
}
- (id)objectForKeyedSubscript:(id<NSCopying>)key {
id obj = _internalDict[key];
if ([obj isKindOfClass:[NSNull class]]) {
return nil;
}
return obj;
}
#end
#implementation NSDictionary <NilSafeConvenience>
- (NilSafeDictionary*)nilSafe {
return [[NilSafeDictionary alloc] initWithDictionary:self];
}
#end
CAVEAT: You don't explain here what problem you're trying to solve, but for anyone who comes across this question in the future, subclassing NSDictionary is almost certainly never what you want.
NSDictionary is a class cluster. Class clusters are tricky to subclass correctly with guidance, plus Apple's documentation explicitly tells you not to subclass, so there's no guidance on how to do it correctly.
Even if you're able to use your subclass and have it "work correctly" for your purposes, every other piece of code in the system will still be returning the original NSDictionary class, not your subclass, so it doesn't benefit you.
If you're hell bent on modifying the way NSDictionary works, create your own wrapper class that has an NSDictionary inside and create your own objectForKeyedSubscript: implementation.
Yes, you can add subscript indexing to your own custom class just like NSDictionary!
dictionary[#key] is short hand for calling [dictionary objectForKeyedSubscript:#"key"]. Therefore to override the functionality of dictionary[#"key"] you can subclass NSDictionary.
#interface MyDictionary : NSDictionary
- (id) objectForKeyedSubscript:(id)key;
#end
#implementation MyDictionary
- (id) objectForKeyedSubscript:(id)key
{
/*
...
*/
}
#end
Original answer using categories, however this approach isn't advised as it will override the original method removing access to super and potential of clashes should multiple categories be loaded overriding the same method. The last category to be loaded will be used at runtime, therefore you cannot guarantee which that will be.
dictionary[#key] is short hand for calling [dictionary objectForKeyedSubscript:#"key"]. Therefore to override the functionality of dictionary[#"key"] you would create a new category for NSDictionary.
//Category in NSDictionary+CustomKeyedSubscript.h and NSDictionary+CustomKeyedSubscript.m
#interface NSDictionary (CustomKeyedSubscript)
- (id) objectForKeyedSubscript:(id)key;
#end
#implementation NSDictionary (CustomKeyedSubscript)
- (id) objectForKeyedSubscript:(id)key
{
/*
...
*/
}
#end

Subclassing iOS Model Objects - Appropriate Design Pattern

I fear this is a rather simple question, but after much googling I think I have overshot my intended result. I believe my question to be related to a design pattern, but alas I could be wrong.
My application calls an RESTful API and gets back what amounts to a list of model objects represented by an NSDictionary. Each of which I will call NNEntity. There are (conceptually) multiple different subtypes of NNEntity. All subtypes of NNEntity share the property of entityID, but each have their own unique properties as well. All instances of NNEntity have a method called readFromDict:(NSDictionary *)d that populates their respective properties. This method is enforced by a protocol that all NNEntity subtypes conform to. It looks like this:
//NNEntity.h
#interface NNEntity : NSObject <NNReadFromDictProtocol>
#property (nonatomic, strong) NSString *entityID;
#end
//NNEntity.m
#implementation NNEntity
- (void)readFromDict:(NSDictionary *)d {
//set common properties from values in d
self.entityID = [d objectForKey:#"ID"];
}
#end
//NNSubEntity1.h
#interface NNSubEntity1 : NSEntity <NNReadFromDictProtocol>
#property (nonatomic, strong) NSString *favoriteColor;
#end
//NNSubEntity1.m
#implementation NNSubEntity1
- (void)readFromDict:(NSDictionary *)d {
[super readFromDict:d];
//set unique properties from values in d
self.favoriteColor = [d objectForKey:#"colorPreference]:
}
#end
//NNSubEntity2.h
#interface NNSubEntity2 : NSEntity <NNReadFromDictProtocol>
#property (nonatomic, strong) NSString *middleName;
#end
//NNSubEntity2.m
#implementation NNSubEntity2
- (void)readFromDict:(NSDictionary *)d {
[super readFromDict:d];
//set unique properties from values in d
self.middleName = [d objectForKey:#"middleName]:
}
#end
I have read various pieces on the use of a Factory or Builder Desing pattern for similar use cases but I am curious if that is necessary in this rather simple case. For example, does my current code end up creating both and instance of NNEntity and NNSubEntity2 if I were to call something like this:
NNEntity *newEntity = [[NNSubEntity2 alloc] init];
//assume dict exists already and is properly keyed
[newEntity readFromDict:dict];
I assume not, but would newEntity have both the common property of entityID as well as the unique property of middleName set correctly? Also, much appreciated if you have thoughts on a better or more efficient design approach.
This looks like exactly how you should be doing it. You have a base class which read in the common attributes, and subclasses which read in their specific attributes.
For example, does my current code end up creating both and instance of NNEntity and NNSubEntity2? NNEntity *newEntity = [[NNSubEntity2 alloc] init];
Nope. When you run this, you instantiate NNSubEntity2 and store the result in a variable typed by it's superclass, which is totally valid. This allows you to call any methods defined on the superclass, but the actual instance is still of the subclass.
Would newEntity have both the common property of entityID as well as the unique property of middleName set correctly?
It sure would. It inherits the instance variables, properties and methods in the superclass.
Rest assured, as far as I can tell this looks sound and is a pattern I've used before.
I do it like this.
// NNEntity.h
#interface NNEntity : NSObject
#property (nonatomic, retain) NSString *entityId;
#end;
// NNEntity.m
#implementation NNEntity
#end;
// NNEntity+KVC.h
#interface NNEnity (KVC)
-(void)setValue:(id)value forUndefinedKey:(NSString *)key {
#end
// NNEntity+KVC.m
#implementation NNEntity (KVC)
-(void)setValue:(id)value forUndefinedKey:(NSString *)key {
// Handle this as appropriate to your app.
// A minimal implementation will throw an exception.
}
#end
And similarly for your various subclasses. You don't (necessarily) need the category on your subclasses.
Then, given NSDictionary *dict with your stuff in it:
NNEntity *entity = [[NNEntity alloc] init];
[entity setValuesForKeysWithDictionary:dict];
Violá! You're done. There are some criticisms of this method, but given a strong implementation of setValue:forUndefinedKey:, I think it's safe and incredibly flexible.
The secrets are in Apple's beautiful Key-Value Coding technology. Essentially, setValuesForKeysWithDictionary: iterates the keys the dict you give it, and for eachinvokes setValue:forKey: in its receiver. It looks something like this (though I'm sure Apple optimizes it under the hood):
-(void)setValuesForKeysWithDictionary:(NSDictionary *)dictionary {
NSArray *keys = [dictionary allKeys];
for (NSString* key in keys) {
[self setValue:[dictionary valueForKey:key] forKey:key];
}
}
I also like this approach because a conversion to CoreData is simple; when you tell CoreData to 'render' your model, it simply overwrites your stubbed model classes, keeping your KVC Category intact. What is more, if your implementation of setValue:forUndefinedKey: is smooth, you can make model changes to your backend without crashing the app (this is a bit of a no-no, but it's not much different from your factory solution).
Of course, I have not addressed your need to selectively choose which class to instantiate. But that is a larger design issue that could be affected even by the design of your API and backend. So I defer.
Also, as you noted in your comment below, the property names must match up. This is a show-stopper for some developers, especially so if you cannot control both the backend and the client.
Give it a try. Feedback is welcome.

Instance Variables for Objective C Categories

I have a situation where it seems like I need to add instance variables to a category, but I know from Apple's docs that I can't do that. So I'm wondering what the best alternative or workaround is.
What I want to do is add a category that adds functionality to UIViewControllers. I would find it useful in all my different UIViewControllers, no matter what specific UIViewController subclass they extend, so I think a category is the best solution. To implement this functionality, I need several different methods, and I need to track data in between them, so that's what led me to wanting to create instance methods.
In case it's helpful, here's what I specifically want to do. I want to make it easier to track when the software keyboard hides and shows, so that I can resize content in my view. I've found that the only way to do it reliably is to put code in four different UIViewController methods, and track extra data in instance variables. So those methods and instance variables are what I'd like to put into a category, so I don't have to copy-paste them each time I need to handle the software keyboard. (If there's a simpler solution for this exact problem, that's fine too--but I would still like to know the answer to category instance variables for future reference!)
Yes you can do this, but since you're asking, I have to ask: Are you absolutely sure that you need to? (If you say "yes", then go back, figure out what you want to do, and see if there's a different way to do it)
However, if you really want to inject storage into a class you don't control, use an associative reference.
Recently, I needed to do this (add state to a Category). #Dave DeLong has the correct perspective on this. In researching the best approach, I found a great blog post by Tom Harrington. I like #JeremyP's idea of using #property declarations on the Category, but not his particular implementation (not a fan of the global singleton or holding global references). Associative References are the way to go.
Here's code to add (what appear to be) ivars to your Category. I've blogged about this in detail here.
In File.h, the caller only sees the clean, high-level abstraction:
#interface UIViewController (MyCategory)
#property (retain,nonatomic) NSUInteger someObject;
#end
In File.m, we can implement the #property (NOTE: These cannot be #synthesize'd):
#implementation UIViewController (MyCategory)
- (NSUInteger)someObject
{
return [MyCategoryIVars fetch:self].someObject;
}
- (void)setSomeObject:(NSUInteger)obj
{
[MyCategoryIVars fetch:self].someObject = obj;
}
We also need to declare and define the class MyCategoryIVars. For ease of understanding, I've explained this out of proper compilation order. The #interface needs to be placed before the Category #implementation.
#interface MyCategoryIVars : NSObject
#property (retain,nonatomic) NSUInteger someObject;
+ (MyCategoryIVars*)fetch:(id)targetInstance;
#end
#implementation MyCategoryIVars
#synthesize someObject;
+ (MyCategoryIVars*)fetch:(id)targetInstance
{
static void *compactFetchIVarKey = &compactFetchIVarKey;
MyCategoryIVars *ivars = objc_getAssociatedObject(targetInstance, &compactFetchIVarKey);
if (ivars == nil) {
ivars = [[MyCategoryIVars alloc] init];
objc_setAssociatedObject(targetInstance, &compactFetchIVarKey, ivars, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[ivars release];
}
return ivars;
}
- (id)init
{
self = [super init];
return self;
}
- (void)dealloc
{
self.someObject = nil;
[super dealloc];
}
#end
The above code declares and implements the class which holds our ivars (someObject). As we cannot really extend UIViewController, this will have to do.
I believe it is now possible to add synthesized properties to a category and the instance variables are automagically created, but I've never tried it so I'm not sure if it will work.
A more hacky solution:
Create a singleton NSDictionary which will have the UIViewController as the key (or rather its address wrapped as an NSValue) and the value of your property as its value.
Create getter and setter for the property that actually goes to the dictionary to get/set the property.
#interface UIViewController(MyProperty)
#property (nonatomic, retain) id myProperty;
#property (nonatomic, readonly, retain) NSMutableDcitionary* propertyDictionary;
#end
#implementation UIViewController(MyProperty)
-(NSMutableDictionary*) propertyDictionary
{
static NSMutableDictionary* theDictionary = nil;
if (theDictionary == nil)
{
theDictioanry = [[NSMutableDictionary alloc] init];
}
return theDictionary;
}
-(id) myProperty
{
NSValue* key = [NSValue valueWithPointer: self];
return [[self propertyDictionary] objectForKey: key];
}
-(void) setMyProperty: (id) newValue
{
NSValue* key = [NSValue valueWithPointer: self];
[[self propertyDictionary] setObject: newValue forKey: key];
}
#end
Two potential problems with the above approach:
there's no way to remove keys of view controllers that have been deallocated. As long as you are only tracking a handful, that shouldn't be a problem. Or you could add a method to delete a key from the dictionary once you know you are done with it.
I'm not 100% certain that the isEqual: method of NSValue compares content (i.e. the wrapped pointer) to determine equality or if it just compares self to see if the comparison object is the exact same NSValue. If the latter, you'll have to use NSNumber instead of NSValue for the keys (NSNumber numberWithUnsignedLong: will do the trick on both 32 bit and 64 bit platforms).
This is best achieved using the built-in ObjC feature Associated Objects (aka Associated References), in the example below just change to your category and replace associatedObject with your variable name.
NSObject+AssociatedObject.h
#interface NSObject (AssociatedObject)
#property (nonatomic, strong) id associatedObject;
#end
NSObject+AssociatedObject.m
#import <objc/runtime.h>
#implementation NSObject (AssociatedObject)
#dynamic associatedObject;
- (void)setAssociatedObject:(id)object {
objc_setAssociatedObject(self, #selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)associatedObject {
return objc_getAssociatedObject(self, #selector(associatedObject));
}
See here for the full tutorial:
http://nshipster.com/associated-objects/
It mentioned in many document's online that you can't create create new variable in category but I found a very simple way to achieve that. Here is the way that let declare new variable in category.
In Your .h file
#interface UIButton (Default)
#property(nonatomic) UIColor *borderColor;
#end
In your .m file
#import <objc/runtime.h>
static char borderColorKey;
#implementation UIButton (Default)
- (UIColor *)borderColor
{
return objc_getAssociatedObject(self, &borderColorKey);
}
- (void)setBorderColor:(UIColor *)borderColor
{
objc_setAssociatedObject(self, &borderColorKey,
borderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
self.layer.borderColor=borderColor.CGColor;
}
#end
That's it now you have the new variable.
Why not simply create a subclass of UIViewController, add the functionality to that, then use that class (or a subclass thereof) instead?
Depending on what you're doing, you may want to use Static Category Methods.
So, I assume you've got this kind of problem:
ScrollView has a couple of textedits in them. User types on text edit, you want to scroll the scroll view so the text edit is visible above the keyboard.
+ (void) staticScrollView: (ScrollView*)sv scrollsTo:(id)someView
{
// scroll view to someviews's position or some such.
}
returning from this wouldn't necessarily require the view to move back, and so it doesn't need to store anything.
But that's all I can thinkof without code examples, sorry.
I believe it is possible to add variables to a class using the Obj-C runtime.
I found this discussion also.

Objective-C: Accessing Member Objects that are in an array?!? I think

Totally new to Obj-C, so thanks for patience. :P
Because I'm beginner, I will use the car example. Easier for me to understand.
I have an object, Car. It has two member objects, tire and engine.
Tire and engine have their own member variables too, but they are just int with various names (like pressure, treadDepth).
In all these cases, I have synthesized accessor methods. I'm not sure about accessor methods for objects, so I just did #property id engine / #property id tire. Hope that is right!
Now, I can do dot.notation style to access like: [car.engine cylinders]. Fine! Sending tire and engine messages works fine. I write methods, this notation seems to work.
But when I declare an array of objects like 4 tires for the car:
#interface Car : NSObject {
tire *tires[4];
}
I cannot send it message like this
[car.tire[0] setPressure: int];
It says accessing unknown tires getter method.
Basically I am wondering if someone can help me understand how to correctly access member variables of an object that is in an array.
Thanks!
You are trying to call a getter on car that doesn't exist. You can't return a C-style array by value anyway, so instead of just returning a Tire* pointer i'd rather use a NSArray in this case:
// header:
#interface Car : NSObject {
NSArray *tires;
}
#property (nonatomic, copy) tires;
// ...
// source:
#implementation Car
#synthesize tires;
- (id)init {
if ((self = [super init])) {
tires = [[NSArray alloc] initWithObjects:
[[[Tire alloc] init] autorelease],
// ...
nil];
// ...
}
return self;
}
- (void)dealloc {
[tires release]; // don't forget to clean up
// ...
}
Now you could use the getter:
[[[car.tires] objectAtIndex:0] setPressure:0];
Why not put all of your tire objects into an NSArray or NSSet? Or, since you know there are only four, you could simply define frontLeftTire, frontRightTire, etc. properties.
Well you could use Objective-C style arrays. Then you would have something like:
NSArray *tires = [NSArray arrayWithObjects: tire1, tire2, tire3, tire4];
And then you would access them as:
[tires objectAtIndex:0];
That's assuming you are using the synthesized methods as described. I'm not sure from your question, but it seems like you might want to define a class "tire" for these objects (rather than just a method, which is all I see above) that inherits from NSObject, or maybe from your own class CarPart, etc. Then you allocate 4 tires in a loop and call an init method that sets up some default state (hopefully better than the donut that came as the spare in my car) and then add them to your array in "Car" when you initialize a car.

setPrimitiveValue:forKey: and to-many relationships

Apple's documentation on -setPrimitiveValue:forKey: is vague in two ways when using it to manage to-many relationships.
First they state:
If you try to set a to-many relationship to a new NSMutableSet object, it will (eventually) fail.
Eventually?! What does that even mean? Will it fail later during -[NSManagedObjectContext save:]? When an managed object is turned into a fault and then paged back in? When? Can I write a test case to consistently recreate the failure on-demand?
Second, providing sample code to correctly handle this case, they write:
first get the existing set using primitiveValueForKey: (ensure the method does not return nil)
What should I do if/when the method does return nil? assert() it and fail immediately because that means the entire object graph is corrupted and saving will lead to data loss? NSAssert() on it as a warning to the caller but press on (silently doing nothing)?
Right now I'm simply directly assigning my desired NS[Mutable]Set in that case, like so:
- (void)setChildren:(NSSet*)value_ {
NSMutableSet *mutableRelationshipSet = [[[self primitiveValueForKey:#"children"] mutableCopy] autorelease];
if (mutableRelationshipSet) {
[mutableRelationshipSet setSet:value_];
[self setPrimitiveValue:mutableRelationshipSet forKey:#"children"];
} else {
[self setPrimitiveValue:value_ forKey:#"children"];
}
}
Is that wrong?
Just mutate the return value of -primitiveValueForKey: as a mutable set, trusting that its return value will do the right thing.
Be sure to also use -willChangeValueForKey:withSetMutation:usingObjects: and -didChangeValueForKey:withSetMutation:usingObjects: around your manipulation; you didn't show this in your above code.
- (void)setChildren:(NSSet *)value {
[self willChangeValueForKey:#"children" withSetMutation:NSKeyValueSetSetMutation usingObjects:value];
NSMutableSet *primitiveValue = [self primitiveValueForKey:#"children"];
[primitiveValue setSet:value];
[self didChangeValueForKey:#"children" withSetMutation:NSKeyValueSetSetMutation usingObjects:value];
}
If you can target Leopard, you don't have to worry about this; you can use Core Data's built-in property support instead:
#import <CoreData/CoreData.h>
#interface Folder : NSManagedObject
#property (readwrite, retain) NSSet *children;
#end
#implementation Folder
#dynamic children;
#end
Core Data will generate not only a getter/setter pair for the children property but the mutable-set accessors as well, so you can use -mutableSetValueForKey: on it and manipulate the result with a minimum of overhead.