I have an object which inherits from NSObject & I wish to save it in CoreData, I have already create an attribute of type "Transformable" (which is "id"), so I cannot save the project directly when adding it to the database, for example:
item.idObject = object;
it will generate an error, I think the problem is that I should transform object into id,
how can I do this?
You're on the right track by setting the attribute to transformable. But what you also have to to is to write a valueTransformer for this object that Core Data can use.
There is more in the Documentation
when you assign a core data property, it may only be of certain types, NSString, NSNumber etc... if you assign a relationship, it must be another NSManagedObject instance, if you want to save it as part of the model, you can of course have transient properties of any type.
so...
#implimentation OurClass
#dynamic someProperty; // core data model property NSNumber, NSString, etc.
#dynamic someRelationship; // core data relationship some other subclass of NSManagedObject
#synthesized someOtherProperty; // can be anything, following normal Objective-C semantics.
#end
Related
What is the difference between transient and derived properties of a core data entity? I would like to create a "virtual" property that can be used in a fetch operation to return localized country names from a core data entity.
The operation is to be done this way:
retrieve the country name from the database in english
do a NSLocalizedString(countryNameInEnglish, nil) to obtain the localized country name.
2 is to be done by this "virtual" property.
Which one should I use? transient or derived and how do I do that?
I have nothing to show you because I have no clue what I should use.
thanks
According to Apple's guide for Non-Standard Persistent Attributes:
You can use non-standard types for persistent attributes either by using transformable attributes or by using a transient property to represent the non-standard attribute backed by a supported persistent property. The principle behind the two approaches is the same: you present to consumers of your entity an attribute of the type you want, and “behind the scenes” it’s converted into a type that Core Data can manage. The difference between the approaches is that with transformable attributes you specify just one attribute and the conversion is handled automatically. In contrast, with transient properties you specify two attributes and you have to write code to perform the conversion.
I suggest using transient attributes. Idea is that you create 2 string attributes: countryName (non-transient) and localizedCountryName (transient):
And then, in your NSManagedObjectSubclass, you simply implement a getter for localizedCountryName:
- (NSString *)localizedCountryName
{
NSString *result;
if ([self.countryName length] > 0) {
result = NSLocalizedString(self.countryName, nil);
}
return result;
}
In my CoreData model, I have an entity called Contact. It has an attribute called profileImage, with the type set to Transformable.
In the Contact class (a subclass of NSManagedObject), I've changed profileImage from being a generic id to being an UploadedImage:
#property (nonatomic, retain) UploadedImage * profileImage;
The UploadedImage class has a few properties of its own.
The problem is that CoreData doesn't know when the properties on the UploadedImage object have changed. If only those properties are changed, the willSave method is never called on the Contact object when the managed object is saved. It works as expected if I change any other property on the Contact object: it just doesn't "see" changes to anything on the UploadedImage object within the Contact object.
What's the correct way to indicate that the managed object needs to be saved? Should I manually call willChangeValueForKey: and didChangeValueForKey: for the key 'profileImage' on my Contact object?
Edit to clarify a couple things
Contact is a CoreData entity, but the ImageUpload class is NOT.
CoreData will save the data in the ImageUpload object (because I implemented the NSCoding protocol methods, I think). If I change a property on the ImageUpload object, AND change a property on the Contact object, the Contact is saved, and that changed value persists on the ImageUpload object when the Contact is loaded from CoreData again.
Unless there's another way I'm not aware of, it sounds like marking the NSManagedObject as dirty is the way to go. You can do that using a separate dirtyFlag attribute, or by simply using the profileImage attribute itself.
One way to automate this would be KVO. You observe all properties of UploadedImage in the Contact class, and then just call self.profileImage = self.profileImage. I'm not sure if this is optimized by the compiler. If it is, you can also call willChangeValueForKey: and didChangeValueForKey:, which should trigger it. If Core Data noticed that the object didn't actually change, you can try to implement NSCopying and assign a copy of the original UploadedImage.
I don't believe Core Data will persist changes made to the subclass. The contract in Core data is between the parent entity and Core Data, so Core Data only responds to changes made to the properties in the entity class.
You can get around this by making that property a NSDictionary in the Entity and in the subclass add the image object to the dictionary.
Expanding upon Scott's solution (no copy necessary!), here is a method for Swift that works with any field:
static func markDirty<C, T>(_ obj: C, _ keyPath: ReferenceWritableKeyPath<C, T>) {
obj[keyPath: keyPath] = obj[keyPath: keyPath]
}
Used like so:
NSManagedObject.markDirty(playlist, \.rules)
Unfortunately it's not possible right now to define it as an instance method, due to limitations with generics.
Is it possible to map a property of a Core Data model from one type to another using only a Core Data Mapping Model or do you need to define a custom Mapping Policy?
For example lets say I have a model called Show with the property identifier of type Integer 32 and I want to map it to a String. This is just a hypothetical example. You can of course do this in code as needed in the ManagedObject subclass.
I am looking to know if it is possible in general.
You can always convert properties from one type to another programatically. Taking your given example:
// in your NSManagedObject subclass
#property (nonatomic) int32_t identifier;
// someplace else
NSNumber *identifierNumber = [NSNumber numberWithInt:managedObject.identifier];
NSString *identifierString = [identifierNumber stringValue];
I would like to add a NSDictionary into a NSManagedObject Category class (or the NSManagedObject class itself)
When I do this, and I try to access the property, an exception is thrown.
Is this actually possible? I can't add this property as transient in the model because there is no NSDictionary Data Type, of course.
Thanks!
You don't say how you have currently created the property or what the exception is, but from the description you give it sounds like you should be setting the attribute in the Core Data model to be transformable. Setting it to be transformable will cause the NSDictionary to be archived (and unarchived) as you use it using the standard NSCoding protocol. Be sure that everything you put into the dictionary supports the NSCoding protocol so that it is properly archived and restored.
Using transformable is the way. Below are few more insights on the transformable property.
The Transformable data type is a special data type that allows us to
create attributes based on an Objective-C class (custom objects). This
data type is heavily used for storing instances of UIImage, UIColor,
and so on. As the information stored in the persistent store has to be
in the form of NSData instance, while using Transformable data type,
we need to create Value Transformers to convert the custom object
(information in attribute of Transformable data type) into an instance
of NSData (before storing in the persistent store) and to convert the
instance of NSData back to custom object while retrieving from the
persistent store.
I'm starting to create an application with Core Data, to retrieve a data for sectioned table i want to use NSFetchedResultController, in the example from apple there are two additional properties.
primitiveTimeStamp
primitiveSectionIdentifier
For the case of primitiveSectionIdentifier apple says that
In contrast, with transient properties you specify two attributes and
you have to write code to perform the conversion.
because the sectionidentifier is transient property.
But what about the timeStamp ?this attribute is not a transient, why there is a primitiveTimeStamp property ? and why there is explicit setter for timeStamp ?
- (void)setTimeStamp:(NSDate *)newDate {
// If the time stamp changes, the section identifier become invalid.
[self willChangeValueForKey:#"timeStamp"];
[self setPrimitiveTimeStamp:newDate];
[self didChangeValueForKey:#"timeStamp"];
[self setPrimitiveSectionIdentifier:nil];
}
or maybe it's not a actual setter? where is _timeStamp=newDate?
CoreData generates the accessors for you. It generates "public and primitive get and set accessor methods for modeled properties".
So in this case it has generated:
-(NSDate*)timeStamp;
-(void)setTimeStamp:;
-(NSDate*)primitiveTimeStamp;
-(void)setPrimitiveTimeStamp:;
"why there is a primitiveTimeStamp property ?"
The declaration is merely to suppress compiler warnings. ie. If you removed the declaration of the property you'd find a warning on compilation but the code would still run.
Or alternatively you could use [self setPrimitiveValue:newDate forKey:#"timeStamp"];
"why there is explicit setter for timeStamp ?"
This is required since setting the timeStamp requires the 'sectionIdentifier' to be recalculated. This is achieved by setting it no nil and letting the get accessor recalculate it lazily.
"where is _timeStamp=newDate?"
The equivalent of this is essentially done in the auto generated implementation of setPrimitiveTimeStamp.
A quote from the docs:
By default, Core Data dynamically creates efficient public and primitive get and set accessor methods for modeled properties (attributes and relationships) of managed object classes. This includes the key-value coding mutable proxy methods such as addObject: and removes:, as detailed in the documentation for mutableSetValueForKey:—managed objects are effectively mutable proxies for all their to-many relationships.
Note: If you choose to implement your own accessors, the dynamically-generated methods never replace your own code.
For example, given an entity with an attribute firstName, Core Data automatically generates firstName, setFirstName:, primitiveFirstName, and setPrimitiveFirstName:. Core Data does this even for entities represented by NSManagedObject. To suppress compiler warnings when you invoke these methods, you should use the Objective-C 2.0 declared properties feature, as described in “Declaration.”