I am adding object exercise to object session (as a relationship).
In a view, I want to fetch and display exercises for a particular session object.
Right now it is showing all exercises in the database rather than just for that session object.
The relationship between the two objects is called "exercises".
This is the current code I am using for the fetch if anyone can help me.
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Exercise" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
LogResultsViewController *logResultsTableViewController = [[LogResultsViewController alloc]init];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat: #"exercises = %#", logResultsTableViewController.selectedSession]];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
Updated Code:
- (void) viewDidLoad
{
NSSet *exercises = [self.selectedSession valueForKey:#"exercises"];
NSArray *sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"timeStamp" ascending:YES]];
NSArray *sorted = [exercises sortedArrayUsingDescriptors:sortDescriptors];
sorted = self.exerciseArray;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [exerciseArray count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [exerciseArray objectAtIndex:indexPath.row];
return cell;
}
When I NSLog the selectedSession, here is what it shows:
selectedSession: <Session: 0x7336940> (entity: Session; id: 0x7334f70 <x-coredata://17D44726-23F7-402F-9CBE-2EED96212E14/Session/p1> ; data: {
exercises = "<relationship fault: 0x5d34680 'exercises'>";
timeStamp = "2011-05-31 04:41:07 +0000";
Also, when I NSLog the NSSset called exercises, I get:
Relationship fault for (<NSRelationshipDescription: 0x711b370>), name exercises, isOptional 1, isTransient 0, entity Session, renamingIdentifier exercises, validation predicates (
), warnings (
), versionHashModifier (null), destination entity Exercise, inverseRelationship exercises, minCount 0, maxCount 0 on 0x7140790
Update:
Ok so I changed the code in cellForRowAtIndex to have
Exercise *exercise = (Exercise *)[exerciseArray objectAtIndex:indexPath.row];
cell.textLabel.text = exercise.name;
Now it shows the exercise name but it is showing the same list of exercises for all sessions, instead of just for the session to which it belongs to.
If your instances of Session have a relationship to instances of Exercise, you don't need to do another fetch to get the session's exercises. You can just follow the relationship and get them directly-- they'll be loaded automatically. From your code it looks like logResultsTableViewController.selectedSession is an instance of Session. If that's the case, you can get all of that session's exercies as follows (assuming the relationship is called exercises):
NSSet *exercises = [logResultsTableViewController.selectedSession valueForKey:#"exercises"];
You can then sort that NSSet as needed.
Related
I am trying to fetch all categories, and their sub categories, and display them all in a table. I know how to fetch all categories, but I need to fetch all sub categories, and sort them by category using a fetch results controller. Any ideas of suggestions?
You can create a fetched results controller that fetches SubCategory entities and groups them into sections according to the Category:
// Fetch "SubCategory" entities:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"SubCategory"];
// First sort descriptor for grouping the cells into sections, sorted by category name:
NSSortDescriptor *sort1 = [NSSortDescriptor sortDescriptorWithKey:#"category.name" ascending:YES];
// Second sort descriptor for sorting the cells within each section:
NSSortDescriptor *sort2 = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObjects:sort1, sort2, nil];
self.fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:context
sectionNameKeyPath:#"category.name"
cacheName:nil];
[self.fetchedResultsController setDelegate:self];
NSError *error;
BOOL success = [self.fetchedResultsController performFetch:&error];
Then you can use the usual table view data source methods as described in the NSFetchedResultsController Class Reference.
This gives you a table view with one table view section for each category.
so, you have the categories in the fetchedResultsController.fetchedObjects
since each subCategory is essentially contained in the Category you can access each by calling [Category valueForKey:#"subCategory"
this will give you an NSSet that you can then sort out (to an NSArray) and use as data for your tableView.
It won't be contained in a fetchedResultsController though.
If you have the option u can do it in other way also if u like.
Take all The Category objects in arrayOfCategories
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection (NSInteger)section:
{
Category *cat = [ arrayOfCategories objectAtIndex:indexPath.row]
if(arrayToHoldObjects.count > 0)
{
[arrayToHoldObjects removeAllObject];
}
for(Subcategory *sub in Category.subcategory)
{
[arrayToHoldObjects addObject:sub];
}
return arrayToHoldObjects.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Category *cat = [ arrayOfCategories objectAtIndex:indexPath.row]
if(arrayToHoldObjects.count > 0)
{
[arrayToHoldObjects removeAllObject];
}
for(Subcategory *sub in Category.subcategory)
{
[arrayToHoldObjects addObject:sub];
}
Subcategory *sub = [arrayToHoldObjects objectAtIndexPath.row]
for(int k =0 ; k < arrayToHoldObjects .count; k++)
{
// do what ever u like with sub
return cell;
}
}
i'm getting the distance of 2 points like this
[userLocation distanceFromLocation: annotationLocation] / 1000;
and setting this to the subtitle of a tableview like the image bellow
the question is, can i order this table by the distances (subtitle)?
Thanks!
and sorry for the bad english =x
You can order your UITableView's cells any way to want to, but you have to do it before showing them, when you create the table's data source. If you use an NSFetchResultsController, you can put the distance as the sort descriptor. And if you are using a simple NS(Mutable)Array, sort it before making it the table's source.
Like Chris said, you can do it with
NSSortDescriptor *titleSorter= [[NSSortDescriptor alloc] initWithKey:#"annotationLocation" ascending:YES];
If it is an NSArray what you are using, then:
[arrayOfObjects sortUsingDescriptors:[NSArray arrayWithObject:titleSorter];
and if it is an NSFetchResultsController:
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:titleSorter, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
Create an NSSortDescriptor to sort your rows:
NSSortDescriptor *titleSorter= [[NSSortDescriptor alloc] initWithKey:#"annotationLocation" ascending:YES];
[arrayOfObjects sortUsingDescriptors:[NSArray arrayWithObject:titleSorter];
Make an array with these distances, order it how you want and
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"cellIdentifier";
UITableViewCell *_cell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (_cell == nil) {
_cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier] autorelease];
}
_cell.textLabel.text = #"Transcripts";
_cell.detailTextLabel.text = [yourArray objectAtIndex:indexPath.row];
return _cell;
}
well, something like that should do the trick.
Hope it helps
Assuming you have some certain objects holding a coordinate and put the objects in to an array locations, you can use a comparator block by doing:
locations = [locations sortedArrayUsingComparator: ^(id a, id b) {
CLLocationDistance dist_a= [[a objectsForKey:#"coordinate"] distanceFromLocation: userPosition];
CLLocationDistance dist_b= [[b objectsForKey:#"coordinate"] distanceFromLocation: userPosition];
if ( dist_a < dist_b ) {
return (NSComparisonResult)NSOrderedAscending;
} else if ( dist_a > dist_b) {
return (NSComparisonResult)NSOrderedDescending;
} else {
return (NSComparisonResult)NSOrderedSame;
}
}
You add this code to your viewWillAppear: to get updated locations each time you display the tableView.
I am creating Travel guide app using core data. I have 4 entities CITY ,RESTAURANTS ,
HOTEL AND FAMOUS PLACES. City is connected with all other entity because one city may have number of restaurants , hotels and places. City entity has 3 attribute Name ,Image ,Description. I am able to display list restaurants of selected city.In Restaurant Entity I have 4 attribute Name, Description ,Address and phone no..Now I want to show
this attribute detail of selected restaurant(of selected city) in next view.But how can I access restaurant description in next view..
Here is my code.
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSMutableArray *restaurants = [[NSMutableArray alloc]initWithArray:[city.cityTorestaurants allObjects]];
NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc]initWithKey:#"Name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:nameDescriptor,nil];
[restaurants sortUsingDescriptors:sortDescriptors];
[hotels sortUsingDescriptors:sortDescriptors];
[self setArrayOfRestaurants:restaurants];
[restaurants release];
[nameDescriptor release];
[sortDescriptors release];
[tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)atableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil ) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier]autorelease];
}
//set up cell
NSLog(#"For Restaurants List");
Restaurants *restaurants = [arrayOfRestaurants objectAtIndex:indexPath.row];
cell.textLabel.text = restaurants.Name;
return cell;
}
cityTorestaurants and restaurantTocity is relationship in core data..
Help Please..
In your RestaurantViewController create a property Restaurant* currentRestaurant
in your didSelectRowAtIndexPath:of the CityViewController set the property with self.restaurantVC.currentRestaurant = [arrayOfRestaurants objectAtIndex:indexPath.row];
This is kinda confusing so please bear with me.
I have an array (listOfStates) of dictionaries. Where listOfStates has this structure:
{stateName} - {stateCapital}
{Alabama} - {Montgomery}
{Alaska} - {Juneau}
{Arizona} - {Phoenix}
...
{West Virginia} - {Charleston}
{Wisconsin} - {Madison}
{Wyoming} - {Cheyenne}
This is the code used to obtain the 1st letter of each US State(stateName) in order to group them together alphabetically:
listOfStates = [[NSArray alloc] init];
stateIndex = [[NSMutableArray alloc] init];
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
for (NSDictionary *row in listOfStates) {
[tempArray addObject:[row valueForKey:#"stateName"]];
for (int i=0; i<[tempArray count]-1; i++){
char alphabet = [[tempArray objectAtIndex:i] characterAtIndex:0];
NSString *uniChar = [NSString stringWithFormat:#"%C", alphabet];
if (![stateIndex containsObject:uniChar]){
[stateIndex addObject:uniChar];
}
}
}
That code works beautifully, however I'm having issues understanding how to populate a tableview cell with BOTH the #"stateName" and #"stateCapital"(as the subtitle) after I use NSPredicate to sort the array.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
//---get the letter in the current section---
NSString *alphabet = [stateIndex objectAtIndex:[indexPath section]];
//---get all states beginning with the letter---
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", alphabet];
NSArray *states = [[listOfStates valueForKey:#"stateName"] filteredArrayUsingPredicate:predicate];
//---extract the relevant state from the states object---
cell.textLabel.text = [states objectAtIndex:indexPath.row];
//cell.textLabel.text = [[listOfStates objectAtIndex:indexPath.row] objectForKey:#"stateName"];
//cell.detailTextLabel.text = [[listOfStates objectAtIndex:indexPath.row] objectForKey:#"stateCapital"];
return cell;
}
Any help is appreciated and I am more than willing to send the entire project, if you think it'll help you understand.
Thank you so much.
cell.textLabel.text = [[states objectAtIndex:indexPath.row] objectForKey:#"stateName"];
cell.detailTextLabel.text =[states objectAtIndex:indexPath.row] objectForKey:#"stateCapital"];
Update:
Unfortunately, the 'states' array only
includes the state names...
Change the predicate and filter to:
NSPredicate *p=[NSPredicate predicateWithFormat:#"stateName beginswith[c] %#", alphabet];
NSArray *states=[listOfStates filteredArrayUsingPredicate:p];
...that will give you an array of states that all start with the same letter. Then you can sort the array and the row will give you the right state dictionary.
Maybe change the data structure so you have one dictionary with state name as a key and state capital as a value. That way you can sort your keys and get the value using the keys, not indexes.
I've attempted to add the TopSongs parser and Core Data files into my application, and it now builds succesfully, with no errors or warning messages. However, as soon as the app loads, it crashes, giving the following reason:
UPDATE: I've got it all working, but my TableView doesn't show any data, and the app doesn't respond to the following breakpoints.
Thanks.
UPDATE: Here's the new code that doesn't respond to the breakpoints.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)table {
return [[fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (void)viewDidUnload {
[super viewDidUnload];
self.tableView = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:self.managedObjectContext];
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *kCellIdentifier = #"SongCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.font = [UIFont boldSystemFontOfSize:14];
}
Incident *incident = [fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:NSLocalizedString(#"#%d %#", #"#%d %#"), incident.title];
return cell;
}
- (void)tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[table deselectRowAtIndexPath:indexPath animated:YES];
self.detailController.incident = [fetchedResultsController objectAtIndexPath:indexPath];
[self.navigationController pushViewController:self.detailController animated:YES];
}
UPDATE: Here's the code where all instances of fetch are found.
- (Category *)categoryWithName:(NSString *)name {
NSTimeInterval before = [NSDate timeIntervalSinceReferenceDate];
#ifdef USE_CACHING
// check cache
CacheNode *cacheNode = [cache objectForKey:name];
if (cacheNode != nil) {
// cache hit, update access counter
cacheNode.accessCounter = accessCounter++;
Category *category = (Category *)[managedObjectContext objectWithID:cacheNode.objectID];
totalCacheHitCost += ([NSDate timeIntervalSinceReferenceDate] - before);
cacheHitCount++;
return category;
}
#endif
// cache missed, fetch from store - if not found in store there is no category object for the name and we must create one
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:self.categoryEntityDescription];
NSPredicate *predicate = [self.categoryNamePredicateTemplate predicateWithSubstitutionVariables:[NSDictionary dictionaryWithObject:name forKey:kCategoryNameSubstitutionVariable]];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchResults = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
NSAssert1(fetchResults != nil, #"Unhandled error executing fetch request in import thread: %#", [error localizedDescription]);
Category *category = nil;
if ([fetchResults count] > 0) {
// get category from fetch
category = [fetchResults objectAtIndex:0];
} else if ([fetchResults count] == 0) {
// category not in store, must create a new category object
category = [[Category alloc] initWithEntity:self.categoryEntityDescription insertIntoManagedObjectContext:managedObjectContext];
category.name = name;
[category autorelease];
}
#ifdef USE_CACHING
// add to cache
// first check to see if cache is full
if ([cache count] >= cacheSize) {
// evict least recently used (LRU) item from cache
NSUInteger oldestAccessCount = UINT_MAX;
NSString *key = nil, *keyOfOldestCacheNode = nil;
for (key in cache) {
CacheNode *tmpNode = [cache objectForKey:key];
if (tmpNode.accessCounter < oldestAccessCount) {
oldestAccessCount = tmpNode.accessCounter;
[keyOfOldestCacheNode release];
keyOfOldestCacheNode = [key retain];
}
}
// retain the cache node for reuse
cacheNode = [[cache objectForKey:keyOfOldestCacheNode] retain];
// remove from the cache
[cache removeObjectForKey:keyOfOldestCacheNode];
} else {
// create a new cache node
cacheNode = [[CacheNode alloc] init];
}
cacheNode.objectID = [category objectID];
cacheNode.accessCounter = accessCounter++;
[cache setObject:cacheNode forKey:name];
[cacheNode release];
#endif
totalCacheMissCost += ([NSDate timeIntervalSinceReferenceDate] - before);
cacheMissCount++;
return category;
}
And this one...
- (void)fetch {
NSError *error = nil;
BOOL success = [self.fetchedResultsController performFetch:&error];
NSAssert2(success, #"Unhandled error performing fetch at SongsViewController.m, line %d: %#", __LINE__, [error localizedDescription]);
[self.tableView reloadData];
}
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController == nil) {
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Song" inManagedObjectContext:managedObjectContext]];
NSArray *sortDescriptors = nil;
NSString *sectionNameKeyPath = nil;
if ([fetchSectioningControl selectedSegmentIndex] == 1) {
sortDescriptors = [NSArray arrayWithObjects:[[[NSSortDescriptor alloc] initWithKey:#"category.name" ascending:YES] autorelease], [[[NSSortDescriptor alloc] initWithKey:#"rank" ascending:YES] autorelease], nil];
sectionNameKeyPath = #"category.name";
} else {
sortDescriptors = [NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:#"rank" ascending:YES] autorelease]];
}
[fetchRequest setSortDescriptors:sortDescriptors];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:sectionNameKeyPath cacheName:#"SongsCache"];
}
return fetchedResultsController;
}
your extra caching is probably a waste of cycles as Core Data performs its own caching internally. I am willing to bet you are slowing things down rather than speeding them up, not to mention the additional memory you are consuming.
Where are you setting categoryEntityDescription? That is now shown in the code you posted. It is probably nil.
Why are you retaining an NSEntityDescription?!? They are already in memory because of Core Data and retaining them is a waste which could lead to issues if Core Data wants to release it at some point.
update
Your caching is definitely not coming from Apple's code because they know that the cache is in Core Data.
As for the NSEntityDescription, again, do not retain the NSEntityDescription.
Are you 100% positive that the NSEntityDescription is not nil? Have you confirmed it in the debugger? Have you tested it with a freshly retrieved NSEntityDescription?
update
You need to learn to use the debugger as that will solve most of your coding issues. Put a breakpoint in this method and run your code in the debugger. Then when the execution stops on that break point you can inspect the values of the variables and learn what they are currently set to. That will confirm or deny your suspicions about what is and is not nil.
This error you are seeing happens when you fail to set the Entity in the NSFetchRequest which, based on your code, means that retained property is not being set before the code you have shown is being called.
Based on the code posted and the problem description, I suspect that the categoryEntityDescription property is returning nil.
I've seen this happen when the NSEntityDescription given to a fetch request is nil. The most likely cause of that is that you have a model entity that is named differently from the name you provided to entityForName. Barring that, it could be an error in configuration of your Core Data stack or a missing data model, but as a first step, I would recommend storing the result of entityForName in a local variable and breaking there to make sure it isn't nil.
Since you added the model file manually, is the .xcdatamodel file inside the Compile Sources step in your Target?
Go to the Targets entry in the Groups & Files pane in Xcode and click the disclosure triangle. Then click on the disclosure triangle for your app. Then check to see if it's in Compile Sources. If not, right click on Compile Sources and choose "Add -> Existing File..." and add it.
Edit based on update:
UPDATE: Here's the new code that
doesn't respond to the breakpoints.
- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath
- (void)tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Is your view controller set as the UITableViewDataSource/UITableViewDelegate for your UITableView? If not, these methods will not get called.