What exactly is [NSFetchedResultsController sections]? - core-data

I am really confused about this.
I see situations where [[NSFetchedResultsController sections] count] is used.
Then there are these situations, [[self.fetchedResultsController fetchedObjects] count].
Now i understand what the latter one is doing. that just makes sense to me.
But i don't understand the first one regarding the sections.
The document doesn't define sections. It actually defines it by using the term "sections" with its definition.
thanks.

If you set a sectionNameKeyPath: in the initWithFetchRequest:... call, then the fetched results controller will group the results into sections, corresponding to the sections of a table view.
So [[NSFetchedResultsController sections] count]is the number of sections and [[self.fetchedResultsController fetchedObjects] count]is the total number of all fetched objects.

Related

Saving various core data entities of the same type with multiple different relationships?

I've been trying to debug this for 2 hours, have to sleep. First, I've searched and found many similar posts with they keywords: many entities same type and this one:
Core Data: inverse relationship for two relationships with same type
But to no avail. Here's what's happening:
I have a data model with two types. For example, I have a Person, and that person can have 4 lists of emails. The email type is its own thing, so I make the person store 4 distinct sets (relationships) to each list.
Basically, when I retrieve one set, it grabs all of them. All 4 lists are now 1, and it doesn't seem to matter how I set the inverses. Actually these changes somehow caused the entire thing to not save anything, all nil objects when downloaded. Previous to this, it worked fine (except for shoving all 4 lists into one set). All I updated was the data model for relationships. I may have exported the files in the wrong order, but do not know if that's related.
I simply can't find the keyword combination to find if someone's asked this before. I don't see how it wouldn't come up, what I'm doing is basic abstraction. I keep thinking I overlooked one box.
To summarize, I have a person, and person has 2 lists. I add them similar to this:
person.friendEmailsList = downloadedEmailsFromFriends;
person.businessEmailsList = downloadedBusinessEmails;
so later, I access person.friendEmailsList (using the correct core data call, of course), and instead of getting just friends, I get everything... friends, businesses everything
Any suggestions would be appreciated
There are two possible solutions.
First, you can use the approach you use. Just make sure that you also have corresponding reverse relationships from the other entity. So if you have 2 relationships to the same entity, that entity needs 2 distinct relationships back. E.g.
Person.friendsLists --->> List
Person.businessLists --->> List
List.friendPerson ---> Person
List.businessPerson ---> Person
The more flexible approach would be to have the list have an extra attribute type (could be a number as some kind of enum).
typedef enum {Friend = 1, Business } ListType;
You could put this into your List.h file. Now, to access just friend lists you can do this:
NSSet *friendList = [person.lists filteredSetUsingPredicate:
[NSPredicate predicateWithFormat:#"type = %#", #(Friend)]];
This might be a bit much to type, so you can simplify by putting an accessor method into your Person.m (declare it in .h):
-(NSSet*)friendsLists {
return [person.lists filteredSetUsingPredicate:
[NSPredicate predicateWithFormat:#"type = %#", #(Friend)]];
}
Now you can access the lists with the usual convenient person.friendsLists.

How do I access Core Data Attribute validation information from code?

I have a string attribute in a Core Data entity whose Max Length value is 40. I'd like to use this value in code and not have to re-type the value "40." Is this possible?
As #K.Steff says in the comments above, you are better off validating in your code and not setting a max length in your core data model. To add on to that comment, I would also advise you to look at using a custom NSManagedObject subclass for this entity type, and within that subclass overriding the validateValue:forKey:error: or implementing a key-specific validation method for this property.
The value of this approach is that you can do things like "coerce" the validation by truncating strings at validation time. From the NSManagedObject documentation:
This method is responsible for two things: coercing the value into an appropriate
type for the object, and validating it according to the object’s rules.
The default implementation provided by NSManagedObject consults the object’s entity
description to coerce the value and to check for basic errors, such as a null value when
that isn’t allowed and the length of strings when a field width is specified for the
attribute. It then searches for a method of the form validate< Key >:error: and invokes it
if it exists.
You can implement methods of the form validate< Key >:error: to perform validation that is
not possible using the constraints available in the property description. If it finds an
unacceptable value, your validation method should return NO and in error an NSError object
that describes the problem. For more details, see “Model Object Validation”. For
inter-property validation (to check for combinations of values that are invalid), see
validateForUpdate: and related methods.
So you can implement this method to both validate that the string is not too long and, if necessary, truncate it when it is too long.
From NSManagedObject you can access the NSEntityDescription via entity. In there you can grab the array properties and a dictionary propertiesByName, either of which will get you to NSPropertyDescriptions. Each property description has a property, validationPredicates that will return an array of NSPredicates. One of those will be the condition that your string length must be at most 40.
Sadly predicates are a lot of hassle to reverse engineer — and doing so can even be impossible, given that you can create one by supplying a block. Hopefully though you'll just have an NSComparisonPredicate or be able to get to one by tree walking downward from an NSCompoundPredicate or an NSExpression.
From the comparison predicate you'll be able to spot from the left and right expressions that one is string length and the other is a constant value.
So, in summary:
Core Data exposes validation criteria only via the very general means of predicates;
you can usually, but not always, rebuild an expression (in the natural language sense rather than the NSExpression sense) from a predicate; and
if you know specifically you're just looking for a length comparison somewhere then you can simplify that further into a tree walk for comparison predicates that involve the length.
It's definitely not going to be pretty because of the mismatch of the specific and the general but it is possible.

Sorting based on one of many related entities

Entity B (Book) has a one-to-many relationship with the entity D (Description). The idea is that a book has different descriptions for different languages.
I want to sort books based on their titles (D.title) for a given language (D.languageID)
If B had one-to-one relationship to D, I would do something like:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"d.title" ascending:YES];
Of course, you may say, the model does not prevent a book from having many descriptions with the same languageID. But in this case any (e.g. the first) description would be ok for me.
Is my model wrong? What's the best solution now?
This is logically not possible. If you have many objects B belonging to A, which B is the sort routine supposed to take?
For example you have the entity A "Parent" and the entity B "Child" and the attribute of B "dateOfBirth". You cannot sort all Parent objects according to the lowest birth date because if two different parents have two children each, it is possible that each of them has a child that is older than one of the other's.
Clear?

Is there a Core Data predicate for all objects in a linked list?

Is there a way to formulate a Core Data predicate for a given object, representing the head of a singly linked list, and all of the other objects in that list?
E.g., I have objects, each of which has a relationship to another object (say nextObject) and I want a predicate for a specified object and all other objects reachable by traversing nextObject (until it is nil).
CLARIFICATION:
I'm using these for a UITableView's NSFetchedResultsController, so these need to be part of the fetch, not something I iterate through in code.
You wouldn't use a predicate for a linked list. Instead, you would just start with the first object of interest and walk the relationships by calling nextObject until you hit one that did not have a nextObject value.
You can find the first and last objects with a predicate in a fetch just by looking for previousObject==nil and nextObject==nil.
Predicates do not understand arbitrarily long relationship chains. They understand a chain like enity1.entity2.entity3 but not nextObject.nextObject.nextObject... because they have no way of knowing when to stop.

Core Data: inverse relationship for two relationships with same type

In my app Core Data model I have Sheet and Text entities. Sheet entity can have two Text's: privacyNotes and termsOfUse.
Both of Text type. So in XCode data modeler I create to-one relationships called "privacyNotes" and "termsOfUse" in Sheet with Text destination. Next goes to-one relationship "sheet" in Text. Then I select that Text.sheet relationship as inverse for Sheet.privacyNotes. So far so good. But when I set same Text.sheet relationship as inverse for Sheet.termOfUse XCode deletes this relationship as inverse Sheet.privacyNotes!
I understand that relationships in DB can be not so simple compared to Objective-C objects relationships, but I really don't get why SQLite or (CoreData) can't reuse one relationship as inverse for FEW other relationships?
A little peek under the abstraction hood might be enlightening*: a relation can only be the inverse for exactly one other relation because, in the backing store, they're represented by the same data. If a Text and a Sheet can have a certain relationship, Core Data does what a good human data modeler would do and stores that relationship as succinctly as possible. The relation properties of the entity objects are just ways of looking at that relationship.
To get the effect of what you're going for: go ahead and give Sheet properties for privacyNote and termsOfUse; but give Text properties like sheetIAmTermsFor and sheetIAmPrivacyNoteFor, and set them as inverses appropriately. Then in the Text class, add a synthetic property along these lines:
// in interface
#property (nonatomic, readonly) Sheet *sheet;
// in impl
-(Sheet *)sheet
{
if ([self sheetIAmTermsFor])
return [self sheetIAmTermsFor];
else
return [self sheetIAmPrivacyNoteFor];
}
If you want to write a setter too, you'll have to decide which role that setter should bestow on the Text (which Core Data can't figure out for you, another reason a property can't be the inverse of two different properties.)
If you need to enforce a constraint that a Text can only ever be a "privacyNote" or a "terms" but never both, override the setters for sheetIAmTermsFor and sheetIAmPrivacyNoteFor, following Apple's pattern in the docs, and have each null the other property when set.
(* Apple regards the SQLite databases Core Data generates as private to their implementation, but inspecting their schemas can be very educational. Just don't be tempted to write shipping code that goes behind CD's back to poke at the db directly.)
You are far better off having a one to many relationship between Sheet and Text with a validation limit of 2. Then you should have a type property in the text which declares it as either a privacyNotes or termsOfUse. From there you can add convenience methods to your Sheet subclass that allows you to retrieve either one.

Resources