Reputation: 335
I've got a Core Data property that I'm trying to set at runtime, with a value derived from another property. However, curiously, the custom accessor I built never even seems to get called.
The property, seasonNameVisible
, only gets called from a predicate with terms from a search field, as shown here:
// Add our search predicates
for (NSString *term in searchTerms) {
NSPredicate *searchTermPredicate = [NSPredicate predicateWithFormat:@"(episodeName contains[cd] %@) OR (fromSeason.seasonNameVisible contains[cd] %@) OR (fromSeason.fromSeries.seriesName contains[cd] %@)", term, term, term];
[subPredicates addObject:searchTermPredicate];
}
If I change that property to seasonName
, that part of the predicate returns a result just fine, so I'm not suspecting the predicate or any of the other search code.
My plan is to derive the seasonNameVisible
NSString from seasonName
at runtime. So, I've modified the Season
NSManagedObject subclass to override the accessor and setter, using primitive accessors. But as far as I can tell, my accessor never gets called.
Here is the header/interface:
//
// Season.h
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@class Episode, Series;
@interface Season : NSManagedObject
@property (nonatomic, retain) NSNumber * seasonIndex;
@property (nonatomic, retain) NSString * seasonName;
@property (nonatomic, retain) NSString * seasonNameVisible;
@property (nonatomic, retain) NSSet *episodes;
@property (nonatomic, retain) Series *fromSeries;
@property (nonatomic, retain) NSString * primitiveSeasonName;
@property (nonatomic, retain) NSString * primitiveSeasonNameVisible;
@end
@interface Season (CoreDataGeneratedAccessors)
- (void)addEpisodesObject:(Episode *)value;
- (void)removeEpisodesObject:(Episode *)value;
- (void)addEpisodes:(NSSet *)values;
- (void)removeEpisodes:(NSSet *)values;
@end
// my additions
@interface Season (PrimitiveAccessors)
- (NSString *)primitiveSeasonName;
- (NSString *)primitiveSeasonNameVisible;
@end
...and the implementation:
//
// Season.m
//
#import "Season.h"
#import "Episode.h"
#import "Series.h"
@implementation Season
@dynamic seasonIndex;
@dynamic seasonName;
@dynamic seasonNameVisible;
@dynamic episodes;
@dynamic fromSeries;
// my additions
@dynamic primitiveSeasonName;
@dynamic primitiveSeasonNameVisible;
- (NSString *)seasonNameVisible
{
NSString *visible;
[self willAccessValueForKey:@"seasonNameVisible"];
visible = [self primitiveValueForKey:@"seasonNameVisible"];
[self didAccessValueForKey:@"seasonNameVisible"];
if (visible != nil) {
return visible;
} else {
[self willAccessValueForKey:@"seasonName"];
visible = [[self primitiveValueForKey:@"seasonName"] substringFromIndex:2];
[self didAccessValueForKey:@"seasonName"];
[self setSeasonNameVisible:visible];
return visible;
}
}
- (void)setSeasonNameVisible:(NSString *)seasonNameVisible
{
[self willChangeValueForKey:@"seasonNameVisible"];
[self setPrimitiveValue:seasonNameVisible forKey:@"seasonNameVisible"];
[self didChangeValueForKey:@"seasonNameVisible"];
}
@end
I've read Apple's docs, and searched around for help with custom accessor methods on StackOverflow, and I think I have the code right (this is the first time I've ever tried to use primitive accessors, or to override an NSManagedObject method, so I'm a tad out of my usual depth), but even when I put a breakpoint on it, it never seems to get called.
Upvotes: 4
Views: 738
Reputation: 119242
You're asking core data to fetch based on a lazily loaded attribute. I dont think this is going to work. You either need to set your seasonNameVisible when you set the season name or, which may make more sense, separate out the storage of your season name into whatever is the prefix (that you are removing to give the visible name) and whatever is the real name.
After the additional information in the comments, I would advise the following:
The error you are seeing in your comment is normal when altering a fetch request used with an FRC. Delete the app from the simulator and re-build and it will go away, or use a nil cache while developing. The FRC stores its cache permanently (eg between runs) so any changes upset it.
Section name key path can be any key path or property name you like, as long as the sorting is the same. From the docs for NSFetchedResultsController:
sectionNameKeyPath
A key path on result objects that returns the section name. Pass nil to indicate that the controller should generate a single section. The section name is used to pre-compute the section information. If this key path is not the same as that specified by the first sort descriptor in fetchRequest, they must generate the same relative orderings. For example, the first sort descriptor in fetchRequest might specify the key for a persistent property; sectionNameKeyPath might specify a key for a transient property derived from the persistent property.
So, you'd have a Season
object with two persistent attributes, seasonNumber
and seasonName
. You'd sort your fetch request by season number. Your section name key path (for a fetch presumably on episodes, which have a season
relationship) would be @"season.seasonSectionName"
, implemented as follows - no changes in your managed object model, just changes to your Season object:
Season.h:
@property(nonatomic,readonly) NSString *seasonSectionName;
Season.m:
-(NSString*)seasonSectionName
{
return [NSString stringWithFormat:@"%d - %@",self.seasonNumber,self.seasonName];
}
All you're really doing is decorating the season number with another property.
Upvotes: 1