I am hoping to find a way to check, if a CoreData attribute is empty. The attribute itself is of type binary data. If the attribute is empty then I could tell my class to download and save some data into this attribute.
According to CoreData Documentation, you should not keep fetching to see if objects exists. I am wondering if there is even a way to possibly do this? without breaking this 'law'?
This is my first attempt at using CoreData. I am adding it to my code afterwards, which is slightly more painful, but as a whole so far everything seems to be going okay. I just need to figure out a logical way of checking if attribute has values. If it doesn't then I need to download and save the new data, if it does then I just use what's in the attribute.
Update :
I just found this method in the CoreData framework that I have been reading though trying to catch a break on this. Not sure if it would help.. what do you guys think?
willAccessValueForKey: Provides support for key-value observing access
notification.
(void)willAccessValueForKey:(NSString *)key Parameters key The name of one of the receiver's properties. Discussion See
didAccessValueForKey: for more details. You can invoke this method
with the key value of nil to ensure that a fault has been fired, as
illustrated by the following example.
[aManagedObject willAccessValueForKey:nil];
Not sure really.. the things that I dont understand is Provides support for key-value observing access notification. ???
That notification is for when the value is going to be accessed.
If I understand you correctly, you are not wanting to see if an entity exists, but an attribute within the entity. So, I assume you have it marked as an optional attribute.
Let's say you have a binary data attribute called rawData. If you want to find all the #"MyEntity" objects in the database that do not have any data set for this attribute, you cn issue this fetch request.
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"MyEntity"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"rawData = nil"];
NSArray *results = [managedObjectContext executeFetchRequest:fetchRequest error:0];
Related
I am currently working with CoreData.
Problem: I have a CoreData Entity User with a one-to-many-relationship to another CoreData Entity Badges. I am now trying to delete a User and, obviously, also would like to delete all of his Badges.
Deleting the User itself is pretty straight forward:
context.delete(selectedUser)
However I have to first delete all of the User's Badges. This is the complicated Part for me:
for badge in selectedUser.badges {
context.delete(badge)
}
When doing so, this Error occurs: Cannot convert value of type 'NSSet.Element' (aka 'Any') to expected argument type 'NSManagedObject'
My Possible Solution: I was thinking of simple downcasting: context.delete(badge as! NSManagedObject). However I am not sure whether this is possible.
Question: What is the best Practice for achieving the Goal I described above? Or is there maybe a CoreData way to recursively delete all related Objects?
Thanks for your help.
selectedUser.badges is an NSSet of Badges, therefore you can cast its elements to Badge or to NSManagedObject:
for badge in selectedUser.badges {
context.delete(badge as! NSManagedObject)
}
You can also cast the NSSet to its Swift counterpart Set:
for badge in selectedUser.badges as! Set<Badge> {
context.delete(badge)
}
But in order to delete all related objects if a user is deleted, the simple solution is to set the “Deletion Rule” for the relationship to “Cascade”.
How can we validate the context before saving it to core data?. My idea is i should have some validation before saving it into core data, if the values doesn't satisfy the validation the the coredata should not save the values. Say for example i have attributes like name, class, age, etc for entity candidate. I should have a validation that the values shouldn't be nil. If it is nil then the other values should not be saved.
Can anybody help me in this regard
EDITED:
I need to check them only at the time of saving and that should be done with core data
I like to do catchall validation in the save routine. Before you actually do the call to save the context, loop through its insertedObjects array and make sure they are as you require. If they aren't, you can either delete them or alert the user that they need to complete something (if the latter, return out of the method; give the user a chance to fix the problem).
Other validation should be at the point of entry, when you are getting values from, say, a textfield or checkbox to assign to your managed objects. Let the user know right away if there's a problem.
Also check out NSNumberFormatter, which can be applied to fields, preventing user from making incorrect entries to begin with.
Coredata validate itself when inserting its values. In managedObject class we can write our custom validation so that coredata will check that validation before saving the values. If the value is not valid then those values in the context will not be saved to coredata.
Here i added
#interface
-(BOOL) validateForInsert:(NSError **)error;
#implementation
-(BOOL) validateForInsert:(NSError **)error {
// check the value of the field with validation
if(condition == true) {
return Yes;
}
return NO;
}
(NSError **) is a special parameter that makes the coredata to call this method as if like a delegate method
Sorry, I hadn’t read your question carefully enough when I made that first answer. You’re not validating that individual entries for individual attrs are correct, rather, that no changes should be saved unless all attrs are filled for that object.
Looking at Apple doc “Model Object Validation”, you are concerned with inter-property validation, not property validation, and you are on the right track to be thinking of using validateForInsert: for this purpose.
That doc also supplies examples. Here’s a possible implementation for the particular entity you describe:
- (BOOL)validateForInsert:(NSError **)error {
if (self.name && self.class && self.age)
return [super validateForInsert:error];
else
return NO;
}
However, this method happens at the insertion stage, not at the save stage.
If you are gathering entries for a new entity all at once, validating at the insertion stage would make sense — don’t add a new object to the context if that object is doomed to be discarded as incomplete.
If you are gathering entries for changes to an existing object, and you want to make sure that all those changes work together before accepting any of them, validateForUpdate: would make sense — but there would be no way to restore the object to its original state other than by reopening the context without saving, unless you had cached its original values elsewhere.
If you want to gather attrs individually and wait to check that they are all complete before saving the object, I think you would do as I first suggested: Loop through the context’s insertedObjects and take care of validation before actually saving the context. There’s no existing validateForSave: method to override, but you could add one.
You could also combine these techniques: Gather entries and make new objects without inserting them, but cache all these objects in an array. When it comes time to save, loop through the cache and insert the objects into the context only if they pass validateForInsert:; then save the context.
Obviously I’m learning along with you, so the above might not be quite the cookie. Hopefully that Apple doc will be enough to get you started.
I've implemented a small proof-of-concept app using Core Data to accept some object attribute values from the user via text fields and it's all working great thanks to information found here and in the iPhone Core Data Recipes app. But I'm at the point where I need to display object validation errors to the user and I can't find a recommended way of handling this. The code in the Recipe app just logs the error and says to "Replace this implementation with code to handle the error appropriately." Great, thanks.
I'm sure there are a multitude of ways to interpret, parse and transmit the validation error information to the user but what I'd like to know is if there are some best practices or a pattern that someone has implemented that I could follow. Where should the validation code like [newObject valdiateForInsert&error]; be placed? In NSManagedObject subclasses? In the UIViewController that handles the screen the enables the object to be added? Maybe in an app-wide ValidationController?
All the validation errors are returned in the NSError's userInfo, which is a NSDictionary of various NSValidation keys and values. Is there a good way of translating this error info into something that would be helpful to the user? For example, I have a rule in my Core Data model that a certain attribute can only be 3 characters long. If in the process of saving or updating an object I get a validation error, I need to parse out the NSError userInfo and find values for the NSValidationErrorKey (the name of the attribute), the NSValidationErrorValue (the value on the object that caused the error) and the NSValidationErrorPredicate (the rule that was violated, which in this case returns length <= 3.
Is there a good, generally accepted way of gathering and munging this data into something that can be passed back to the user? I'm currently pulling the NSError info into strings and then falling through a series of conditional statements for each attribute that I'm validating, and it's so ugly that I kinda want to puke when I look at it. There has to be a better, cleaner way to consume Core Data validation errors and pass a readable version to the user.
Validations are not there for the user. They are there so the code can maintain the integrity of the object graph. Validation methods are not called by the managed object context until the time at which context is saved. That time might be very distant from the time of input.
However, you can call an objects validation methods directly before you set an attribute. The validation methods have the form:
- (BOOL)validateTimeStamp:(id *)valueRef error:(NSError **)outError;
Suppose you have an attribute name for a managed object subclass PeopleMO. The validation method to check for an empty string might look like:
- (BOOL)validateName:(id *)valueRef error:(NSError **)outError{
BOOL isValid=NO;
NSString *toTest=(NSString *) valueRef;
if (![toTest isEqualToString:#""]) {
isValid=YES;
}
return isValid;
}
You could call it anywhere like:
NSString *newName=// some UI element text
PersonMO *newPerson=//.. insert new PersonMO object
if ([newPerson validateName:newName error:nil]) {
newPerson.name=newName;
}else{
//... inform user name is invalid
// ... possibly delete newPerson object from context
}
This is most useful where you have situations in which the validity of the value of one attribute depends on one or more other attributes of the same object.
Whenever I use setPropertiesToFetch on an NSFetchRequest, the objects that are returned appear to all be faulted. They return YES to isFaulted and when displaying them in the console they appear like this:
< MyEntity: 0x5884850> (entity: MyEntity; id: 0x5886180 <x-coredata://4D96A1CB-187C-4D92-A50C-D639F7E69114/MyEntity/p197> ; data: <fault>),
Whereas if I don't set properties to fetch, every object's properties are visible in the console. Any idea why this is happening?
I've got an answer from the Apple Developer Forums:
https://devforums.apple.com/message/152330#152330
"The object being a fault means it might require a trip to the database, or it may mean that the object is pending lazy initialization and all the necessary data is cached. Tracking your app with the Core Data template in Instruments is the best way to empirically tell the difference.
For setPropertiesToFetch, you are intentionally not fetching all the properties, so you end up with a partial fault. Accessing some properties (the ones you told it to fetch) will work, and accessing others will require a round trip to the database. -isFault is a boolean, so most of the framework treats partial faults like they are just faults."
By default, Core Data will fetch objects as faults the first time they're loaded into the managed object context. It won't populate the attributes until you ask for them. If you need a fetch request to return non-faulted objects, you can use setReturnsObjectsAsFaults:YES
The documentation says that the value from -setPropertiesToFetch: is only used when resultType is set to NSDictionaryResultType.
I have a CoreData entity (waypoint) with a foreign key to another entity (track). When the foreign key is set, or if it is not set, this if statement works fine:
if ([wp track]) {
thirdLabel.text = [NSString stringWithFormat:#"Part of Track: %#", [[wp track] title]];
}
However, if the track that the waypoint is keyed to has been deleted, then [wp track] still evaluates to true, and the program crashes when I set the label text.
So, how do I properly check for this "has been deleted" null value in CoreData?
It sounds like you have an issue with a one-way relationship. The problem you are describing is given in more detail here under "Unidirectional Relationships"
Basically, your waypoint has no way to know that the track was deleted out from under it. The recommended solution is to model the relationship to be bi-directional, which allows Core Data to maintain consistency in your model.
In your specific example, if your 'track' object has an inverse relationship back to the 'waypoint', then when you delete the 'track' object, Core Data will know to update the waypoint to get rid of any dangling relationships. More about this can also be seen at the above link.
It is valid Objective-C to send messages to nil. You'll need to modify your if predicate to read:
if (wp != nil && [wp track]) {
//...
}
You can also check the retainCount of an object, which might return 0 or crash in the case when the object has been deallocated.
In either case, it might be best to send out some kind of notification from whomever destroyed wp to those interested in using it, or at least retain wp until after you're done with it in this code.