Problems casting NSManagedObject to subclass type - core-data

I'm using custom classes for my CoreData stack. Class properties are set properly in the model. There's some points in the app that are abstracted to use setValue... on an NSManagedObject, but I have a few cases where that fails with an NSInvalidArgumentException, specifically when setting a related object; error indicates it wants the specific type, and it's' getting an NSManagedObject, hence the error. So, I thought I would just take the short route and cast my instance prior to the offending call, if it's of a certain entity; like this:
NSManagedObject *addressObject = [NSEntityDescription insertNewObjectForEntityForName:#"Address" inManagedObjectContext:[object managedObjectContext]];
if ([[[object entity]name] isEqualToString:#"Hospital"]) {
Contact *contact = (Contact*)object;
DLog(#"The class of contact is: %#", [contact class]);
contact.Address = addressObject;
}
else{
[object setValue:addressObject forKey:#"Address"];
}
I know, Address shouldn't be capitalized; I inherited this mess... anyway, I would totally expect that the contact object is a Contact, but it's not, it's an NSManagedObject! What am I doing wrong with the cast? Everything I've uncovered says this is the right way to cast, but for some reason, it's not working for me here. Of course, this wouldn't be necessary if the addressObject didn't complain about getting an NSManagedObject instead of a Contact (sorry, Hospital inherits from Contact here), and that's another baffling thing, but first things first. How can I coerce object to type Contact, which it really is?
Here's the relevant trace:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for to-one relationship: property = "Contact"; desired type = Contact; given type = NSManagedObject; value = ...
For completeness sake, the Address class has declaration for Contact as:
#property (nonatomic, retain) NSManagedObject * Contact;
with implementation for Contact as the normal dynamic, like:
#dynamic Contact;
Maybe I need some sleep? ;-) Thanks

The cast isn't your problem. Casting doesn't change what class an object is — just what the compiler thinks it is.
You object is an NSManagedObject but not a Contact instance. In you code the object you have is a Hospital entity. Double-check that the Hospital entity is set to use the Contact class (or a subclass)).
It is important to note that Entity inheritance and Objective-C class inheritance don't have to match. I.e. you can have Hospital be a subentry of Contact and still have the Contact entity map to a Contact class without Hospital map to a subclass of the Contact class. It is valid for the Hospital entity to map to the (same) Contact class or even to NSManagedObject (which is what I suspect you've done).
This may seem confusing but can be very powerful if used correctly.

So yeah, turns out I did need some sleep;-) The problem was the subclass not being included in the target. I know I've experienced similar issues when you have a subclass, and it's in the target, but you forget to define the custom subclass in the model, too.

Related

No Core Data entity subclasses during custom migrations

I have a core data database. I'm performing a custom migration. I have a subclass of NSEntityMigrationPolicy. My policy's migration method buildValueFromSectionFieldManagedObject: is being called by a mapping rule : FUNCTION($entityPolicy, "buildValueFromSectionFieldManagedObject:" , $source).
This part is actually working.
However, the implementation of buildValueFromSectionFieldManagedObject: uses methods in the custom entity NSManagedObject subclass of the $source, which is Choice.
The methods of Choice do not seem to be available to the migration function, and instead it gets just a vanilla NSManagedObject.
When I try to use Choice methods, I get an exception. If I po the choice in the debugger, I get something like this:
<NSManagedObject: 0x600000281860> (entity: Choice; id: 0xd00000000038001a ; data: )
Whereas, out of a migration I would usually see something like this:
<Choice: 0x60800028bdb0> (entity: Choice; id: 0x6080002225a0 ; data: {
Is this just how it is, or is there some way that I can use the entity objects during migration?
Possibly relevant – this particular entity, Choice, is removed during this migration. It does not exist in the target managed object model, but does exist in the source managed object model. However, I don't think this is the case as other entity classes that are in the target model are also unavailable as that class during migration – they have class NSManagedObject and their entity methods are not available.
That is correct, you only have access to basic NSManagedObjects during migration.
Three-Stage Migration
The migration process itself is in three stages. It uses a copy of the source and destination models in which the validation rules are disabled and the class of all entities is changed to NSManagedObject.
From: Core Data Model Versioning and Data Migration Guide
Dave's answer clarified that during a migration, core data object are only available as NSManagedObject instances. You don't get to use their Entity classes.
Worse, if you're using a tool like mogenerator, then any handy logic that you've extended the entity classes with is inaccessible.
Poor solutions
Working with an NSManagedObject directly feels dangerous to me. Using [managedObject valueForKey:#"someKey"] is verbose, but worse, there's no compiler checking that you've got your key name correct, so you might be asking for something managedObject doesn't have. There's also no compiler checking of the returned type either – it could be anything that you can put in to a managed object.
Slightly better is [managedObject valueForKey: NSStringFromSelector(#selector(someKey))] is safer, but horribly verbose and awkward, and still isn't that safe – lots of things might implement the method someKey.
You might also declare your keys as literals:
NSString *const someKey = #"someKey";
[managedObject valueForKey: someKey];
Again – this is slightly safer, less error prone and verbose, but you've got no guarantee still that this managed object has someKey.
None of these approaches will give us access to custom logic, either.
Better solution
What I did instead of this was to define a protocol for the properties that I know my managed object has. Here's are examples for entities Choice and ChoiceType.
#protocol ChoiceProtocol
- (NSSet<id <ChoiceTypeProtocol> > *)choiceTypes;
- (NSNumber *)selected;
- (NSNumber *)order;
#end
#protocol ChoiceTypeProtocol
- (NSNumber *)selected;
- (NSString *)name;
- (NSString *)textCustom;
- (NSNumber *)order;
#end
Now in my migration code, instead of having a custom migration function similar to:
- (NSString *)migratedRepresentationOfChoice:(NSManagedObject *)choice;
I have:
- (NSString *)migratedRepresentationOfChoice:(id <ChoiceProtocol>)choice;
In the body of this function I can use choice exactly as I would any regular object. I get code completion as I type, I get the right syntax highlighting, the compiler will complain if I call a non existent method.
I also get return type checking, so the compiler will complain if I use the NSNumber property selected as an NSString or NSSet. And it'll also be helpful and suggest NSNumber methods as completions while I type.
How about the logic?
This approach doesn't provide the logic you've added to entity classes.
In Swift you might be able to use a protocol extension to achieve that.
I was retiring these entities (hence the migration), so I moved the entity logic functions I needed in to a helper functions of my custom migration. As an example choice.orderedChoiceTypes becomes [self choiceOrderedChoiceTypes:choice].
In future I will probably avoid ever adding logic to NSManagedObject entities. I think it's probably a better plan to put any such logic in domain objects that you build from your managed objects. Further, I will probably avoid defining entity classes and instead only access NSManagedObject instances through a protocol as during the migration. It seems clean, simple, removes magic, and has benefits for testing – not just for migrations.

How can I know the class type of an abstract entity in a NSPredicate?

Using core data I'd like to fetch some data. My model uses some abstract entities, see attached picture, where QuantifiedIngredient is an abstract class.
I'd like to fetch Ingredient entities that have at least one RecipeQuantifiedIngredients, but in the middle is QuantifiedIngredient, which is an abstract class.
How can I do that, how can I test the actual type of an abstract class inside a NSPredicate? Any idea or suggestion?
The only clue I found was:
How can you reference child entity name in a predicate for a fetch request of the parent entity?
Would work a custom property in my QuantifiedIngredient to know if it is a RecipeQuantifiedIngredient? For instance isRecipeQuantifiedIngredient?
Thanks a lot for your help.
If recipe is required in RecipeQuantifiedIngredient, you could try to make a fetch, that checks, if there is any ingredient.recipe. I think, that will work.
The custom property, in kind of flag, will work for you too. You'll just need to set and unset it whenever you add or delete all the recipeQuantifiedIngredient.
I don't want to take the time to translate this into CoreData-speak so here is my thought in SQL:
SELECT * FROM quantifiedIngredients WHERE recipe <> NULL
or something like that. This is essentially Nikita's suggestion of using a flag, except that the 'flag' is the existence of a property. I don't know how CoreData will react when faced with GroceryQuantifiedIngredients that don't have the recipe, I think KVO will throw an exception. You might be so bold as to add a category:
#interface GroceryQuantifiedIngredients (KVOHack)
-(id)recipe;
#end
#implementation GroceryQuantifiedIngredients (KVOHack)
-(id) recipe { return nil; }
#end
This would of course require CoreData to enumerate all quantifiedIngredients, but I presume it will have to do so anyway, and a the return nil should optimize into tiny code. The other consideration is whether this will have a bad effect on the rest of your code; you will have to make that call.
Another idea which pops to mind as I finish this up is to do something like this (I'm getting really loose with my pseudo-code now):
SELECT * FROM quantifiedIngredients WHERE [i respondsToSelector:#selector(recipe)];
See what I mean? I forget whether CoreData lets you play with some kind of cursor when working with predicates or fetchedThingamabobbers, if it does than I think this is your best bet. Anyway it's Sunday afternoon so that stuff is left as a exercise for the reader.
+1 for a good question.

Seemingly inconsistent behavior among NSManagedObject subclasses

I have several subclasses of NSManagedObject. They are all instantiated with code something like this:
MeasurementDescriptor *descriptor = (MeasurementDescriptor *)[NSEntityDescription
insertNewObjectForEntityForName:#"MeasurementDescriptor"
inManagedObjectContext:context];
or like this:
Experiment *experiment = (Experiment *)[NSEntityDescription
insertNewObjectForEntityForName:#"Experiment"
inManagedObjectContext:context];
What is odd, though, is that (from code above)
NSLog(#" descriptor's class = %#", NSStringFromClass([descriptor class]));
prints out 'NSManagedObject', while
NSLog(#" experiment's class = %#", NSStringFromClass([experiment class]));
prints out 'Experiment'.
Does anyone know why this would be? MeasurementDescriptor, unlike my other NSManagedObject subclasses, had no ivars (not including its Core Data properties). Adding an ivar did not change anything. Similarly, MeasurementDescriptor is the only NSManagedObject subclass without 'relationship' properties. Perhaps this accounts for this strangeness...???
The only explaination is that your MeasurementDescriptor subclass is not actually known to the code. The most common causes of this are:
In the data model editor not setting the Class attribute of the entity to the correct class.
Not adding the source file for the subclass to the target.
This is easy to do with Core Data because if it can't find a dedicated subclass it doesn't complain but just returns a generic NSManagedObject initialized with the entity's property key names.

Difference between association and dependency?

In a UML class diagram, what is the difference between an association relationship and a dependency relationship?
From what I know, an association is a stronger relationship than a dependency, but I'm not sure how it is stronger.
Any example would be more than welcome :)
An association almost always implies that one object has the other object as a field/property/attribute (terminology differs).
A dependency typically (but not always) implies that an object accepts another object as a method parameter, instantiates, or uses another object. A dependency is very much implied by an association.
In OOP terms:
Association --> A has-a C object (as a member variable)
Dependency --> A references B (as a method parameter or return type)
public class A {
private C c;
public void myMethod(B b) {
b.callMethod();
}
}
There is also a more detailed answer.
What is the difference between dependency and association?:
In general, you use an association to represent something like a field
in a class. The link is always there, in that you can always ask an
order for its customer. It need not actually be a field, if you are
modeling from a more interface perspective, it can just indicate the
presence of a method that will return the order's customer.
To quote from the 3rd edition of UML Distilled (now just out) "a
dependency exists between two elements if changes to the definition of
one element (the supplier) may cause changes to the other (the
client)". This is a very vague and general relationship, which is why
the UML has a host of stereotypes for different forms of dependency.
In code terms, such things as naming a parameter type and creating an
object in a temporary variable imply a dependency.
...
Dependency is like when you define a method that takes a String(in Java, C#, as string is a object in them) as a parameter, then your class is dependent on String class.
Association is like when you declare a string as an attribute in your class.
then your code is associated with the string class.
String name = null //: is a association.
Dependency - A change in a class affects the change in it's dependent class. Example- Circle is dependent on Shape (an interface). If you change Shape , it affects Circle too. So, Circle has a dependency on Shape.
Association- means there is a certain relationship between 2 objects
(one-one, one-many,many-many)
Association is of 2 types-
Composition
Aggregation
1) Composition- stronger Association or relationship between 2 objects. You are creating an object of a class B inside another class A
public class A {
B b;
public void setB(){
this.b= new B();
}
}
If we delete class A , B won't exist( B object is created inside A only).
Another example -Body & Liver .Liver can't exist outside Body.
2) Aggregation - weaker type of Association between 2 objects.
public class A {
B b;
public void setB(B b_ref){
this.b= b_ref;
/* object B is passed as an argument of a method */
}
}
Even if you delete class A, B will exist outside(B is created outside and passed to Class A)
Another example of this- Man & Car . Man has a Car but Man & Car exist independently.
Here: "Association vs. Dependency vs. Aggregation vs. Composition", you have a great vade mecum with uml class diagrams and code snippets.
The author gives us a list of relationships: Association, Dependency, Aggregation, Composition in one place.
A dependency is very general and lowering complexity is about diminishing dependencies as much as possible.
An association is a strong (static) dependency. Aggregation and Composition are even stronger.
I was always checking this answer as it didn't stick in my mind. I found this one more helpful after reading the accepted answer
Association is when one object just has a link to another and don't use relational object methods. For ruby for example
class User
has_one :profile
end
user = User.first
profile = user.profile
profile.sign_out
It means you can get a profile object from user but user don't use profile's methods inside himself(has no dependency on a Profile's interface).
Dependency means that User has link to another object and call that object's methods inside himself
class User
has_one :profile
def personal_info
profile.info
end
end
Here if Profile's info method will be changed or renamed our Dependent User class also need to be changed.

KVC compliance for numbers in NSManagedObject subclass (CoreData)

I'm trying a basic test of sorting an NSManagedObject subclass. I set up a basic subclass "TestClass" with two attributes: stringField and numberField. They use the standard Obj-C 2.0 accessor protocol:
#interface TestClass : NSManagedObject
#property (retain) NSString *stringField;
#property (retain) NSNumber *numberField;
#end
#implementation TestClass
#dynamic stringField;
#dynamic numberField;
#end
When I try to fetch instances of this entity, I can fetch based on either attribute. However, if I use a sort descriptor, the numberField is said to not be KVC-compliant.
Within the model, I set the numberField to Int64, but I'm confused. I thought the wrapper (NSNumber) would handle the KVC problem. What do I need to do to make this work?
Some initial "Is the computer on?"-type questions:
Does your model specify that the managed object class for your entity is TestClass?
Are you sure you spelled numberField correctly when specifying the key in your sort descriptor?
Is numberField a transient attribute in your model?
These are the common issues that I can think of that might cause such an error when fetching with a sort descriptor, the first one especially.
Also, this won't affect KVC, but your attributes' property declarations should be (copy) rather than (retain) since they're "value" classes that conform to the NSCopying protocol and may have mutable subclasses. You don't want to pass a mutable string in and mutate it underneath Core Data. (Yeah, there's no NSMutableNumber or NSMutableDate in Cocoa, but that doesn't prevent creating MyMutableNumber or MyMutableDate subclasses...)

Resources