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;
}
Related
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.
I want to iterate over the properties of my models in Objective-C. I tried this. Created PropertyUtil class with method classPropsFor:(Class)klass. Please find the attachment. Used objc/runtime.h. The code which I got from the net. In my viewcontroller I am doing this. [PropertyUtil classPropsFor:[self.user class]];. self.user is User model class. What I want to know is how can i iterate over the properties of my models, let's username, password and those values.
You may want to manually list all properties your model has.
Just add a method to your model:
+(NSArray*) propList {
return #[#"prop1", #"prop2"];
}
Then just use key-value coding to get the value
[someObject valueForKey:#"prop1"];
That's pretty straight and simple way if you wish to avoid Obj-C meta functions. Since you add your properties manually anyway, you may also add them in your list as well.
That's of course, if you don't have a large amount of models already and you wish do them all at once.
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.”
I would like to add attributes programmatically to an entity during runtime of my app.
Is this something you would recommend doing or can this lead to issues?
How would I need to combine NSAttributeDescription and NSEntityDescription? I am familiar with creating models using Xcode, but did not do it using NSEntityDescription yet.
It's theoretically possible, but doesn't appear very practical.
You can modify the NSManagedObjectModel programmatically, as well as NSEntityDescription. Note that -setEntities: (NSManagedObjectModel) and -setProperties: (NSEntityDescription) both trigger exceptions if you modify a model that has been instantiated. So you can't modify your existing model's structure. You'd have to create a new one and copy all of your data from the old Core Data stack to the new one based on your new model..
Using NSMutableDictionary is a much saner approach.
This is an article talking about this in great detail. Hope it helps.
I would not do this. If the store becomes incompatible with your model it will just crash. Is this risk really worth the benefit you are trying to create?
I have found that it makes sense to create more (even many more) attributes upfront just "to be on the safe side". The overhead of unused attributes is really minimal, but you get the flexibility of easily adding information to your objects "on the fly".
As pointed out in the comments, one good way to implement that is using a separate entity for attributes and adding them as to-many relationships.
I just used nearly the same technique here: EPPZQueuedObject.h
Although, I think mutate the entity architecture during runtime could lead to incompatiblity issues (an exception actually), when the stored SQLite data won't fit for your initial entities at startup.
So this generic object EPPZQueuedObject is an object of two attributes at all, so I had no intention to use a separate model file only for this purpose. But this structure is not mutating during runtime.
#implementation EPPZQueuedObject
#dynamic creationDate;
#dynamic archivedObject;
+(NSEntityDescription*)entityDescription
{
//Describe EPPZQueuedObject.
NSEntityDescription *entityDescription = [NSEntityDescription new];
entityDescription.name = EPPZQueuedObjectEntityName;
entityDescription.managedObjectClassName = NSStringFromClass(self);
//Describe creationDate.
NSAttributeDescription *creationDateDescription = [NSAttributeDescription new];
creationDateDescription.name = #"creationDate";
creationDateDescription.attributeType = NSDateAttributeType;
creationDateDescription.attributeValueClassName = #"NSDate";
creationDateDescription.defaultValue = nil;
//Describe archivedObject.
NSAttributeDescription *archivedObjectDescription = [NSAttributeDescription new];
archivedObjectDescription.name = #"archivedObject";
archivedObjectDescription.attributeType = NSBinaryDataAttributeType;
archivedObjectDescription.attributeValueClassName = #"NSData";
archivedObjectDescription.defaultValue = nil;
//Add attributes.
entityDescription.properties = #[ creationDateDescription, archivedObjectDescription ];
//Voila.
return entityDescription;
}
#end
More details in the corresponding article: http://eppz.eu/blog/simple-core-data-sample/
I'm working on something similar and I'm thinking about creating a new core data class called "Properties", so I can set my core data objects to have a "relationship to many Properties". Each Property would have core data string-type attributes: "attribute", "type" and "value".
I think that should give enough flexibility to add properties to a core data object in the fly. If I happen to implement this, I will post it here
In my NSManagedObject subclass I have an NSString ivar that splits up into an NSSet of entities. I'd like to be able to set the string and during a call to save, do the split, however, only setting the string will not trigger a dirty flag or a need to save.
You can implement the + (BOOL)contextShouldIgnoreUnmodeledPropertyChanges on you NSManagedObject subclass and return NO rather than the default (YES).
This should then cause the NSManagedObjectContext to be notified of changes properties even if they aren't represented by actual columns in the database.
I assume you mean "attribute" instead of "ivar". Your scheme of having a string being split into a set and then saving the set is perhaps debatable, but I guess that is not the issue here.
Why do you need to have the Managed Object marked as "dirty"? This is really not necessary. Just save it, dirty or not!
I do not know how you check the "dirtiness" of your managed object, but I assume you want this to trigger a save at a certain point. At that point you might just as well as check your own BOOL "dirtyFlag" which you can set as appropriate and keep available for checking.
It is always better to make these kinds of things explicit. Your code will become more readable and transparent.