I have a view controller where I create an NSFetchedResultsController to display a set of CoreData objects in a TableView. While viewing these objects in the tableview, they are updated by calling a RestKit getObjectsAtPath, which processes correctly through a response descriptor and RKEntityMapping to update a field on the CoreData objects. However, this particular entity also have a custom derived field - actually a state machine (TransitionKit based) that reads the state value provided to the entity and re-initializes the state machine with the state provided by the server. However, no matter where I reinitialize the state machine (awakeFromFetch, willSave, key-value observing), this reinitialized state machine is not updated when the copy of the object in the NSFetchedResultsController is used to update the corresponding table cell (when the NSFetchResultsController is notified of the change on that row). To be clear - the value that is updated via the RestKit EntityMapping IS updated, but the state machine (a derived value) is not updated. WHY would this be?
Shouldn't the NSFetchedResultsController's array of objects be notified in a way that allows them to compute their derived values? When I trace in the code, awakeFromFetch in the main thread does not yet contain the updated value, and computing my derived value in willSave or a setter does not seem to create this derived value in the instance of the object that is held by the NSFetchedResultsController.
I've attached my base model code
#import "VCStateMachineManagedObject.h"
@interface VCStateMachineManagedObject ()
@property (nonatomic, strong) TKStateMachine * stateMachine;
@end
@implementation VCStateMachineManagedObject
@dynamic savedState;
@synthesize stateMachine = _stateMachine;
@synthesize forcedState;
-(id)init {
self = [super init];
if(self != nil) {
}
return self;
}
- (BOOL)canFireEvent:(id)eventOrEventName {
return [_stateMachine canFireEvent:eventOrEventName];
}
- (BOOL)fireEvent:(id)eventOrEventName userInfo:(NSDictionary *)userInfo error:(NSError **)error{
return [_stateMachine fireEvent:eventOrEventName userInfo:userInfo error:error];
}
- (void) assignStatesAndEvents:(TKStateMachine *) stateMachine {
[NSException raise:@"Invoked abstract method" format:@"Invoked abstract method"];
}
- (NSString *) getInitialState {
[NSException raise:@"Invoked abstract method" format:@"Invoked abstract method"];
return nil;
}
- (void)awakeFromInsert {
if(self.savedState == nil){
self.savedState = [self getInitialState];
}
[self createStateMachine];
}
- (void)awakeFromFetch {
if(self.savedState == nil){
self.savedState = [self getInitialState];
}
[self addObserver:self forKeyPath:@"savedState" options:NSKeyValueObservingOptionNew context:nil];
[self createStateMachine];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if(![[_stateMachine.currentState name] isEqualToString:self.savedState]){
[self createStateMachine];
}
}
- (void) willSave {
NSLog(@"%@", self.savedState);
[self createStateMachine];
}
// Manually set the state, for restkit object mapping
- (void) setForcedState: (NSString*) state__ {
self.savedState = state__;
}
- (void) setSavedState:(NSString *)savedState__{
[self willChangeValueForKey:@"savedState"];
[self setPrimitiveValue:savedState__ forKey:@"savedState"];
[self didChangeValueForKey:@"savedState"];
[self createStateMachine];
}
- (NSString *) state {
NSString * state = [_stateMachine.currentState name];
return [NSString stringWithFormat:@"%@ %@", state, self.savedState];
}
#pragma mark - State Machine
- (void)prepareStateMachine {
for(TKEvent * event in _stateMachine.events){
[event setDidFireEventBlock:^(TKEvent *event, TKTransition *transition) {
self.savedState = transition.destinationState.name;
}];
}
}
- (void) createStateMachine {
_stateMachine = [TKStateMachine new];
[self assignStatesAndEvents:_stateMachine];
[self prepareStateMachine];
_stateMachine.initialState = [_stateMachine stateNamed:self.savedState];
[_stateMachine activate];
}
@end
@quellish Here's what I see when I trace breakpoints in my managed object.
- I call out to restkit to download new objects
- Restkit downloads objects in a background thread (not the main thread). I see it find the matching object (awakeFromFetch), update the state (setForcedState), and save (willSave), and on this instance of the object I see the createStateMachine method called several times (which is because I have it in all these functions, though that of course has no effect on the instance in the NSFetchedResultsController).
- I then see an object on the main thread get fetched (awakeFromFetch) and go through the same process.
- I then see an object on the main thread get triggered by KVO and again go through the createStateMachine method.
in all causes when I view variables in the state machine, they have been updated to the correct value. but THEN, when NSFetchedResultsController triggers on the change, the state machine IS NOT updated even though the value itself is.
In the next few hours I'm going to trace back through this and give even more specific information about the behavior I'm seeing. I'm also going to add a UUID to each instance of the entity to make sure that the one that is being updated is actually the one that's in the NSFetchedResultsController. Stay tuned.