Understanding Transient properties with NSFetchedResultsController - ios

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.”

Related

Continue using NSManagedObject types but migrate off Core Data

I am working with a massive, existing code base that uses Core Data.
I have types that extend NSManagedObject and are persisted in Core Data.
I need to migrate off of Core Data entirely (the reasons for this don't matter, but I have good reasons). However, I have a fundamental constraint.
I cannot change these types to NSObject (some use cases must continue using Core Data).
These NSManagedObject types are heavily passed around my business logic. I don't want to refactor that business logic and introduced a new/"unmanaged" type.
Let's say I have some type Foo that's an NSManagedObject. I tried something like:
Foo *foo = [[Foo alloc] init];
foo.name = "beebunny";
The foo.name call causes a crash.
name is #dynamic and has a custom set method, something like:
- (void)setFoo:(Foo *)fooIn
{
[self setFoo:fooIn];
}
The [self setFoo:fooIn]; call causes an exception (unknown selector).
It seems like I have to use Core Data if I'm working with any type that extends NSManagedObject.
Is there a proper/recommended pattern for the type of migration I'm wanting to perform off of Core Data?
Instances of NSManagedObject depend very heavily on the data model. You don't have to save the instances with Core Data, but they must have a data model backing them up or they won't work. Your [[Foo alloc] init] doesn't work because (a) it doesn't use a designated initializer, and (b) it doesn't have a data model supporting it.
You can create instances that you don't save. For example you can use -initWithEntity:insertIntoManagedObjectContext: to create an instance, but have the context argument be NULL. It'll never be saved unless you insert it, but it sounds like you won't do that. But that initializer requires an NSEntityDescription, and you need to get that from a managed object model. (You can also create them in code, but that's not going to make it easier or remove the need to import Core Data into your code).
In short, you don't have to save these objects to Core Data, but you do need to have some Core Data support if you'll still be subclassing NSManagedObject. You can't use those classes independently of the data model.

Difference between transient and derived properties of a core data entity

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;
}

Get NSManagedObject attribute validation regular expression

In my data model, some of the attributes have regular expressions used for data validation. There are places in my code that I would like to use those same regular expressions.
In the interest of keeping my common regular expressions in one place, I was hoping either to set these regexes in code or to retrieve them from the data model in code.
Is there a way to do this?
I want to access the Reg. Ex. property, shown below, in code.
From a NSEntityDescription you can get its attributes with the method attributesByName. Then you can use the NSPropertyDescription methods validationPredicates and setValidationPredicates:withValidationWarnings:. I assume that a predicate is created under the hood when you set the validation regex in your datamodel file...
I am not completely sure about this, but I think you can only set these values when you are creating your core data model, not once you have your core data stack set up. Is that what you want to do?
Absolutely. Everything you do in the model editor can be done or modified in code by manipulating your NSManagedObjectModel object.
Locate where the model is retrieved in your core data stack setup (maybe in your app delegate). Before returning the model, modify it in code, using constants you can #define in a central include file.
Read all about the object model's API here. More precisely, you set the model's entities after modifying an entity description, by changing the validationPredicates of one of its attributes.
I marked e1985's answer as accepted, since that's the answer that led me here. Here's the code I used to get the predicate. It's in a category for NSEntityDescription.
- (NSPredicate*)getValidationPredicateForAttribute:(NSString*)attributeName
{
NSAttributeDescription* emailAttribute = [self.attributesByName objectForKey:attributeName];
NSArray* validationPredicates = [emailAttribute validationPredicates];
if(validationPredicates.count > 0)
{
return [validationPredicates objectAtIndex:0];
}
return nil;
}

How to override hash and isEqual for NSManagedObjects?

We have a bunch of NSManagedObjects of various types.
Some of them have members that are NSSet's of other NSManagedObjects.
The problem is that I really need to override the hash and isEquals methods of the objects that are IN the set - but they are NSManagedObjects.
I'm having problems with getting multiple identical objects in the set.
As far as I can tell, since hash defaults to the object address - all objects are different. So I need to override hash and isEquals - but can't see any way to do it.
What we have is a bunch of stuff in the System, and more comes in via XML - sometimes repeats of the existing objects. When they are the same, I don't want dups added to the set.
As mentioned above by Wain, NSManagedObject documentation states that you must not override hash or isEqual:. So this means a stock NSSet does not do what you need.
Some of your options are:
Enumerate the NSSet contents to identify and remove duplicates
Write a factory method for your NSManagedObjects that will return the same object when given the same inputs
Fix the XML to not include duplicated objects
Unique the objects coming from the XML before they become NSManagedObjects
Modify the input XML to include a unique identifier that you can track, assuming the duplicated objects are exact duplicates
Implement your own NSSet-like collection class that performs a different uniquing test than hash and isEqual:

Is it posible to conditionally disable NSValueTransformer for NSManagedObject attribute?

Specifically, say I have an NSManagedObject with a "statusCode" attribute set to transformable, and a reversible value transformer subclass to covert from NSStrings to NSNumbers and vice versa. The idea is to use the value transformer so that I receive JSON and a string from a "status" key in the JSON automatically maps to an NSNumber that represents that status code in an NSManagedObject. Conversely if I were to upload the NSManagedObject to a server, at that point its status attribute would be transformed from an NSNumber to a string for the JSON.
So far so good. But, what if I also want to be able to get a simple int out of the NSManagedObjec's status property, so that I can AND it with enums in code?
That is, I'd lie to cover 3 cases:
myManagedObject.status = [JSONResponse valueForKey:#"status"] (should use transformer to do NSString -> NSNumber)
[JSONforUpload setValue:myManagedObject.status forKey:#"status"] (should use transformer to do NSNumber->NSString)
From elsewhere in code, anything along the lines of: if(myManagedObject.status & statusInProgress) ... where statusInProgress is an enum.
I'm thinking I could temporarily disable the value transformer, however I have no idea if the NSManagedObject has a reference to it, or if I should disable it from the NSValueTransformer class, which apparently keeps a table of registered transformers?
I know that for the 3rd case I could just do [myManagedObject.status intValue] and then do the bitwise comparison, but I'm wondering if there's any way I can have the intValue] be returned automagically, from the user of this object's point of view.
Any ideas?
Why don't you just write two additional methods for the JSON transform and leave the property as integer? Then you'd have the best from both worlds.
One approach would be to add a property to the transformer so that it switches between string and enum reversed values. That would work, though I ended up doing a enum<->string transformer and not using it over a transformable attribute (instead I left the managed object's attribute as int) but rather instantiating it only for the JSON <-> object conversion. After that, throughout code I just use the int attribute as is.
Assuming that this entity has its own distinct managed object subclass, you could also simply add another pair of accessor methods to the class to encapsulate the conversion between NSNumber and int values. (Or add a transient attribute, if it needs to be part of the model. But you'd still need to write custom accessors to synch up the values.)

Resources