I need to reload a Person NSManagedObject before I pass it onto the next View.
This is because the fetchedResultsController I'm using is only returning a subset of attributes and I need the full set in the next view.
Thus far I'm trying something like:
- (void)tableView:(UITableView *)tableViewPassed didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
Person *partialPerson = (Person *)[self.fetchedResultsController objectAtIndexPath:indexPath];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Person" inManagedObjectContext:[partialPerson.managedObjectContext]];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entity];
...
Now I can't seem to get the predicate to do this working correctly so far I've tried:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF == %#", partialPerson];
and
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF == %#", partialPerson.objectID];
But neither of these seem to work. What am I doing wrong here? Is this a good approach?
Thanks in advance for any suggestions,
Matt
You just need to access the attributes that are not faulted and they will get faulted automatically. You do not need to refetch the object at all.
Update
Storing images or any binary data in Core Data has some basic rules to follow:
< 100kb store it in the same entity
< 1 mb store it in a separate entity on the other end of a relationship
> 1 mb store it on disk and reference it in the same entity
Sounds like you are storing too much binary data in the primary table. To correct this follow the rule above and it will solve your problem.
But that does not negate my original answer in that you can instruct your fetch to pull in attribute1, attribute3 and attribute 5 and when you need to access attribute3, you just access it and Core Data will "do the right thing" and load it when you try and access it. There is never a reason to "reload" the object in this situation.
Why you need to refetch partialPerson?
Simply pass it to the next view and you are done!!! You can do all you want with partialPerson variable on the next view. I don't see any reason why you need to refetch it.
myViewController.myPartialPerson = partialPerson;
Related
So i'm displaying some Entities in a UITableView. With clicking on a Cell i want to show other Entities that are already queried in a "to-many" Relationship.
For example i'm displaying all Classes of a School. Now i want to display all Students of a Class. This Students are already available as an NSSet under Class.students
Now i want to display the Students in different Sections following by their first Letter.
If i wanted to get them directly from CoreData, i would do something like
// init fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Student" inManagedObjectContext:self.managedObjectContext];
// Search only specific students
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"class == %#", theClassThoseStudentsBelongTo];
[fetchRequest setPredicate:predicate];
// Generate it
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"firstLetter"
cacheName:#"StudentTable"];
with this method i would get them nicely arranged into sections.
But i already have all students for a specific Class. is there a way to init a NSFetchedResultsController with a initialized NSSet or to do something equal?
Sure, i could arrange my NSSet manually but isn't there such a nice way like it is for a new query?
thanks in advance.
Please leave a comment if something is unclear.
I guess you only have 2 options: using NSFetchedResultsController or sorting the objects on your own.
NSFetchedResultsController & NSPredicate:
Pros: easy object deletition; notifications of model changes, e.g. during syncing
Cons: unnecessary refetch
NSSet & NSSortDescriptor
Pros: no refetch
Cons: complicated deletition; no notifications of model changes, e.g during syncing: you could be displaying a student that has already been deleted
you could use a NSPredicate which uses the reverse relationship (from your students back to the class)
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
...
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"class == %#", theClassThoseStudentsBelongTo];
[fetchRequest setPredicate:predicate];
NSFetchedResultsController *theFetchedResultsController = ...
The relationship of objects fetched by NSFetchedResultsController start out as faults and are only fetched when needed. This means that if we don't use the "Student" entities for the first tableView it will be lazily loaded only when we need it.
However, since you need to know the count of students the situation is a little more complicated since calling [class.students count] will fire the fault. (Calling the KVO #count will fire the fault also).
So you have two options:
managed an attribute called studentsCount in class that reflects the number of entities in students. Calling this attribute will not fire a fault on the relationship.
use countForFetchRequest:
NSFetchRequest *req = [[NSFetchRequest alloc] init];
[req setEntity:[NSEntityDescription entityForName:#"Student" inManagedObjectContext:context]];
[req setPredicate:[NSPredicate predicateWithFormat:#"class = %#", myClass]];
The second option performs a fetch, but a very efficient one, so maybe it's efficient enough - i didn't do performance tests so i can't really say.
By the way, if you're not sure whether or not the relationship fired a fault you can use the method hasFaultForRelationshipNamed.
Is there a method similar to insertNewObjectForEntityName that edits the current entity being passed in managed object context? I don't want to create another duplicate entity.
In addition, I don't want users to be able to enter two entities with identical attributes (one attribute, the event title). How can I make it so an alert pops up when they try to add a new entity with an identical title attribute?
Your first question it sounds like what you want to do is get an object that is already in the context with a fetch request, change some values on the object then call the -save method on your context.
For the second part, what you would do is when the user tries to add an item, search the context for an object with the same title, if the item exists, pop up an alert.
Edit: here is some code from my app (edited a bit) in which I set up and execute a fetch request:
NSFetchRequest *categoryRequest = [[NSFetchRequest alloc] init];
[categoryRequest setEntity:[NSEntityDescription entityForName:#"Category" inManagedObjectContext:[self managedObjectContext]]];
NSString *categoryName = #"Cooking";
NSPredicate *categoryNameMatchesPredicate = [NSPredicate predicateWithFormat:#"name MATCHES %#", categoryName];
[categoryRequest setPredicate:categoryNameMatchesPredicate];
NSError *error = nil;
NSArray *categoryArray = [[self managedObjectContext] executeFetchRequest:categoryRequest error:&error];
After this request, the array categoryArray contains all category entities with the name "Cooking". If there are no entities with the name "Cooking" the array will be empty.
It is probably faster to use -countForFetchRequest:error: and check for nonzero count before you actually execute the fetch request, but I am not sure it matters that much in a smallish iOS app.
I have a question based on this link, just to show you what it look like
http://www.techotopia.com/index.php/An_iPhone_OS_Core_Data_Tutorial
Scroll down to almost the bottom of the page to see how the user interface look like.
My problem is that I tried to retrieve the text that I typed in and saved it via save button. After retrieving the text, I want to present it on the screen when I open application next time(on UILabel or whatever). There should be some code in viewDidLoad but I'm clueless on how to retrieve the text or anything else I save.thanks in advance
There are a variety of ways you could approach this issue, including:
1.You could fetch an array of objects to display, based on some criteria (see below). A dateSaved/dateModified attribute might fill such a role.
2.You could put a flag (BOOL) on your CoreData object and fetch based on that flag (modify below). You'd likely want to clear that flag at some point.
3.You could store the value you'd like to display in NSUserDefaults as defaultObject (or something like that).
Here's a little code snippet that may help...
- (NSArray *)findContactsNamed:(NSString *)aName inContext:(NSManagedObjectContext *)aContext
{
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:#"Contacts" inManagedObjectContext:aContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(name = %#)", aName];
[request setPredicate:pred];
return [context executeFetchRequest:request error:nil];
}
I have a "Song" Entity and a "Tag" entity and they have a many to many relationship between them. A Song can have multiple Tags and a Tag can be applied to multiple Songs.
I want to check if a Song has a particular Tag associated with it. If the Song has the Tag associted with it, I want to show a checkmark in the table view.
For a similar logic, in Apple "TaggedLocations" sample code, the following check is made to check for the presence of the relationship.
if ([event.tags containsObject:tag]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
This may be inefficient if there are a lot of Tags in the database as this will fetch all of them in the memory. Please correct me if I am wrong here.
Is there a more efficient way to check if the Song is associated with a particular Tag instead of checking in Song.Tags?
It's actually pretty easy to do, if completely undocumented. You want to create a fetch request with a predicate that has a set operation. If we imagine that your Tag model has a property called tagValue, the predicate you care about is "ANY tags.tagValue == 'footag'"
NSString *tagSearch = #"footag";
// However you get your NSManagedObjectContext. If you use template code, it's from
// the UIApplicationDelegate
NSManagedObjectContext *context = [delegate managedObjectContext];
// Is there no shortcut for this? Maybe not, seems to be per context...
NSEntityDescription *songEntity = [NSEntityDescription entityForName:#"Song" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:songEntity];
// The request looks for this a group with the supplied name
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY tags.tagValue == %#", tagSearch];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
[request release];
You are correct, using that code will retrieve the entire set and the object comparison may be quite complex, depending on how many properties and relationship are part of the object's entity.
Anyway, you can not avoid a set comparison for inclusion. Probably, the best you can do is to avoid fetching all of the properties/relationships by asking Core Data to retrieve NSManagedObjectID Objects only.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Tag" inManagedObjectContext:[self managedObjectContext]]];
[fetchRequest setResultType:NSManagedObjectIDResultType];
NSManagedObjectID objects are guaranteed to be unique, therefore you can safely use them to check for set inclusion. This should be much more efficient for a performance perspective.
I'm new at using CoreData and I'm trying to understand how to perform a query on a table. I can use a fetch request to pull all of the records from a table, but I'm looking for a subset. Is there an easy way to do this?
Thanks,
Howie
Have you looked into Predicates?
Also, buy Marcus Zarra's book on Core Data.
You can add a NSPredicate to the NSFetchRequest to filter the records that are returned. You can also control what is populated in the returned objects (only populate properties, include relationships, populate nothing, just return a count, etc.) but as Peter pointed out, Core Data is an object hierarchy and model API that just happens to store to a database. It is far easier to work with when you look at it from that POV.
You have to do something like:
// Init your fetchRequest
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"entityName" inManagedObjectContext:yourManagedObjectContext];
// create the relation between request and the created entity
[fetchRequest setEntity:entityDescription];
// Set your predicate for this request
// For more info take a look at NSPredicate Class Reference
// http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSPredicate_Class/Reference/NSPredicate.html
[fetchRequest setPredicate:somePredicate];
// Pushing the results into a mutable array
NSMutableArray *mutableFetchResults = [[yourManagedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
[fetchRequest release];