NSFetchedResults Return old data after persisting MOC

Context

I have a simple Core Data: stack MainQueueMOC -> PrivateBackgroundMOC -> PersistentStoreCoordinator, which is controlled by mine TTPersistenceManager, which looks like this:

typedef NS_ENUM(NSInteger, TTPersistenceType) {
    TTPersistenceTypeInMemory,
    TTPersistenceTypeSQLite
};

@interface TTPersistenceManager : NSObject

@property (strong, nonatomic, readonly) NSManagedObjectContext *managedObjectContext; // this is the MainQueueMOC

- (id)initWithPersistenceType:(TTPersistenceType)persistenceType;
- (void)initializeCoreData;

- (void)save;
- (void)persist;

Currently, we only use memory in memory.

This is inspired by this article from Marcus Zarra . Thus, MainQueueMOC is the only source of truth, and PrivateBackgroundMOC is used only for storage in the background, and it is never published publicly. If you read the article, you will notice that I added a method called persist, the difference between saveand persist:

  • savesaves MainQueueMOC with performBlockAndWait
  • persistsaves MainQueueMOC with performBlockAndWaitand PrivateBackgroundMOC withperformBlock

- :

, , , . , , .

, .

, save - , , , persist , .

, .

a NSFetchedResultsController , :

item.kind = "relationship" AND item.relationship.archived == NO

, . , relationship.archived = @YES, [TTPersistenceManager save] NSFetchedResultsController, . .

.

- (void)applicationDidEnterBackground:(UIApplication *)application {
    [self.persistenceController persist];
}

persist, , .

relationship.archived = @YES; // item at index 0 is associated with this object
[self.persistenceManager save];
[self.fetchedResultsController performFetch:&error]; // Works, no error

item = [[self.fetchedResultsController] fetchedObjects] firstObject];
NSLog(@"item is archived %d", item.relationship.archived);
// prints: item is archived 1

relationship archived, YES, .

. , , , , , , sqlite.

\ 1. updatedAt item, , relationship:

 relationship.item.updatedAt = [NSDate date];
 relationship.archived = @YES;
 [self.persistenceManager save];

\ 2. persist save:

relationship.archived = @YES;
[self.persistenceManager persist];

?

, MOC, ?

item, ?


:

@implementation TTPersistenceManager

- (id)initWithPersistenceType:(TTPersistenceType)persistenceType {
    self = [super init];
    if (self) {
        _persistenceType = persistenceType;
    }
    return self;
}

- (void)initializeCoreData {
    FCYAssert(!self.managedObjectModel, @"CoreData has already been initialized");

    NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    self.managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:@[bundle]];
    self.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];

    NSError *error = nil;
    NSPersistentStore *store = [self.persistentStoreCoordinator addPersistentStoreWithType:[self storageType] configuration:nil URL:nil options:nil error:&error];
    FCYAssert(store != nil, @"Failed create persistent store: %@\n%@", [error localizedDescription], [error userInfo]);

    self.persistentStoreManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    self.persistentStoreManagedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator;
    self.persistentStoreManagedObjectContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;

    self.managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    self.managedObjectContext.parentContext = self.persistentStoreManagedObjectContext;
    self.managedObjectContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;
}

- (void)persist {
    if (![self saveContext:self.managedObjectContext]) return;
    [self saveContext:self.persistentStoreManagedObjectContext];
}

- (void)save {
    [self saveContext:self.managedObjectContext];
}

#pragma mark - Private

- (NSString *)storageType {
    if (self.persistenceType == TTPersistenceTypeSQLite) return NSSQLiteStoreType;
    return NSInMemoryStoreType;
}

- (BOOL)saveContext:(NSManagedObjectContext *)context {
    NSError *error = nil;
    BOOL didSave = [self saveContext:context error:&error];

    if (!didSave) {
        TTLogError(@"Error saving context: %@\n\nUser Info:\n%@\n\nCall Stack:\n%@", error.localizedDescription, error.userInfo, [NSThread callStackSymbols]);
    }

    return didSave;
}

- (BOOL)saveContext:(NSManagedObjectContext *)context error:(NSError **)errorPtr {
    __block BOOL hasChanges = NO;

    [context performBlockAndWait:^{
        hasChanges = [context hasChanges];
    }];

    if (!hasChanges) return YES;

    __block NSError *error = nil;
    __block BOOL didSave = NO;

    [context performBlockAndWait:^{
        didSave = [context save:&error];
    }];

    if (!didSave && error && errorPtr) {
        *errorPtr = error;
    }

    return didSave;
}

@end

NSFetchedResultsController

- (NSFetchedResultsController *)setupFetchedResultsController {
    if (!_fetchedResultsController) {

        NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[TTInboxItem entityName]];
        fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:TTInboxItemAttributes.sortDate ascending:NO]];

        NSPredicate *itemPredicate = [NSPredicate predicateWithFormat:@"%K == %@", TTInboxItemAttributes.type, [TTRelationship entityName]];
        NSPredicate *notArchivedPredicate = [NSPredicate predicateWithFormat:@"%K.%K != %@", TTInboxItemRelationships.relationship, TTRelationshipAttributes.archived, @YES];
        NSPredicate *notArchivedPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[itemPredicate, notArchivedPredicate]];

        fetchRequest.predicate = notArchivedPredicate;

        self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                                            managedObjectContext:self.persistenceManager.managedObjectContext
                                                                              sectionNameKeyPath:nil
                                                                                       cacheName:nil];

        self.fetchedResultsController.delegate = self.tableViewBatchUpdater;
        [self fetchInboxItems];
    }

    return _fetchedResultsController;
}
+4
2

. .

Apple:

, . - , : . , , .

MainQueueMOC -> PrivateBackgroundMOC -> PersistentStoreCoordinator, - [TTPersistenceManager.managedObjectContext save]. , save , .

" , MOC, ?" , , , , - applicationDidEnterBackground:, . , , . .

Apple:

, ( hasChanges) save:. Core Data .

if (TTPersistenceManager.managedObjectContext.hasChanges) {
    [TTPersistenceManager.managedObjectContext save]
}

, ?

, , . , . Msgstr " , YES, ." , , , fetchRequest .

+1

, , MOC . , NSManagedObjectContextDidSaveNotification ? .. , (!),

0

All Articles