Determine if saving NSManagedObjectContext will result in changes to persistent store

Determine if saving NSManagedObjectContext will result in changes to persistent store

nspersistentcontainer
core data merge contexts
nsmanagedobjectcontext example
core data save multiple objects
core data child context
core data background thread swift
core data swift
nsfetchrequest

In other words, is it possible to determine if there are changes that would be lost by discarding the current context? Right now my UI indicates the risk of data loss if context.hasChanges == TRUE, but I think hasChanges looks something like this under the hood:

- (BOOL) hasChanges {
    return self.updatedObjects > 0 || self.insertedObjects.count > 0 || self.deletedObjects.count > 0;
}

...and updatedObjects contains objects that were simply edited, even if properties weren't changed from their original values. updatedObjects also also contains objects with transient changes. Saving these objects will not modify the persistent store.

I don't want to prompt my users to save if they haven't actually changed anything, so what's the best way to determine if there are changes to persistent properties?


Since you only care about whether changes exist, rather than finding out exactly what has changed, I'd use the hasPersistentChangedValues method on NSManagedObject to filter the context's fields. For example,

NSPredicate *hasChanges = [NSPredicate predicateWithFormat:@"hasPersistentChangedValues = YES"];
BOOL changesExist = ([[context.updatedObjects filteredSetUsingPredicate:hasChanges] count] > 0);

The same logic would apply for inserted and deleted objects.

NSManagedObjectContext, If a context's parent store is another managed object context, fetch and save use parent contexts created with the thread confinement type (see Concurrency). Changes are not saved to the persistent store until the root context is saved. store migration, Core Data will create instances of NSManagedObjectContext for � When you save changes in a context, the changes are only committed “one store up.” If you save a child context, changes are pushed to its parent. Changes are not saved to the persistent store until the root context is saved. (A root managed object context is one whose parent context is nil.) In addition, a parent does not pull changes from children before it saves.


Maybe you could use committedValuesForKeys: to get the old values of the supposedly-changed keys and compare to the new values. Something like this:

@interface NSManagedObject (tofustew)
- (BOOL)isActuallyUpdated;
@end

@implementation NSManagedObject (tofustew)

- (BOOL)isActuallyUpdated {
    NSArray *keys = self.changedValues.keyEnumerator.allObjects;
    NSDictionary *newValues = [self dictionaryWithValuesForKeys:keys];
    NSDictionary *oldValues = [self committedValuesForKeys:self.changedValues.keyEnumerator.allObjects];
    return [oldValues isEqualToDictionary:newValues];
}

@end

@interface NSManagedObjectContext (tofustew)
- (BOOL)hasActualChanges;
@end

@implementation NSManagedObjectContext (tofustew)

- (BOOL)hasActualChanges {
    if (self.insertedObjects.count > 0 || self.deletedObjects.count > 0)
        return YES;
    for (NSManagedObject *object in self.updatedObjects) {
        if (object.isActuallyUpdated) {
            return YES;
        }
    }
    return NO;
}

@end

refresh(_:mergeChanges:), NSManagedObjectContext; refresh(_:mergeChanges:) Updates the persistent properties of a managed object to use the latest values from the If the staleness interval (see stalenessInterval ) has not been exceeded, any The set of objects that will be removed from their persistent store during the next save operation. In one part of the app, I have code which is supposed to make changes to an NSManagedObject, then call the save method of a context. I thought this was enough to save the changes to the managed object, but I guess not. The app seems to be saving the data to my iPad's RAM, but not to the SQLite store it's supposed to be saving the data to.


This SO post addresses a similar question:

Identifying which fields have changed before CoreData saves

And the relevant Apple docs: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObject_Class/#//apple_ref/occ/instm/NSManagedObject/changedValues

This method only reports changes to properties that are defined as persistent properties of the receiver, not changes to transient properties or custom instance variables. This method does not unnecessarily fire relationship faults.

It's as easy as filtering updatedObjects like so:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"changedValues[SIZE] > 0"];
NSSet *objectsWithChangedValues = [context.updatedObjects filteredSetUsingPredicate:predicate];

A couple lines of code produces the desired result, but I'm still confused as to why updatedObjects works the way it does...

Multiple Managed Object Contexts with Core Data Tutorial , In this tutorial, you'll learn about multiple managed object contexts by taking a journaling managed object subclass and persistent store coordinator don't ring any bells, or if As you can see, the sample app works and has data. When you save a child context, the changes only go to the parent context. Saving NSManagedObject Instances The creation of NSManagedObject instances does not guarantee their persistence. After you create an NSManagedObject instance in your managed object context, explicitly save that context to persist those changes to your persistent store.


I ended up writing the following method in Swift:

/// Checks whether there are actually changes that will change the persistent store.
/// The `hasChanges` method would return `true` for transient changes as well which can lead to false positives.
var hasPersistentChanges: Bool {
    return !insertedObjects.isEmpty || !deletedObjects.isEmpty || updatedObjects.first(where: { $0.hasPersistentChangedValues }) != nil
}

Combined with the following saveIfNeeded method this leads to only necessarily saves:

/// Saves the context, only if it has any changes and if it has a place to save the changes to (parent or persistent store). Calling this method instead of the regular save() will increase performance. It is also useful to use this function when having a memory store, since this configuration doesn't have a persistent store but may use parent contexts to save their changes to.
/// - throws: A Core Data NSError when saving fails.
/// - returns: Whether the save was needed.
@discardableResult public func saveIfNeeded() throws -> Bool {
    let hasPurpose = parent != nil || persistentStoreCoordinator?.persistentStores.isEmpty == false
    guard hasPersistentChanges && hasPurpose else {
        // Saving won't do anything now, except for decreasing performance. Skip it for now.
        return false
    }

    try save()

    return true
}

Getting Started with Core Data Tutorial, Checking the Use Core Data box will cause Xcode to generate This will give the sample app a title style that matches Apple's stock apps. Core Data provides persistence, meaning it can store data in a more durable state so it You commit your changes to person and save to disk by calling save on the� The persistent store is responsible for serializing all access to the data stored on the file system. Therefore only one persistent store should ever be initialized per file. When the stack is being initialized, the store accepts a NSManagedObjectModel and a url for the location of the store on the filesystem.


Reading and Updating Managed Objects With Core Data, The persistent store coordinator handled the nitty-gritty details of inserting the list managedObjectContext: NSManagedObjectContext) -> NSManagedObject? Managed Object result = NSManagedObject(entity: entityDescription, We can fix that by updating the list record and saving the changes. Sometimes NSManagedObjectContext have problems with synchronizing database changes; Saving Files to Disk. Apple makes writing, reading, and editing files inside the iOS applications very easy. Every application has a sandbox directory (called Document directory) where you can store your files.


Core Data Performance: 6 tips you should know, You can save this background context on a custom persistent container subclass Doing so would result in a crash and potential data corruption. Saving a managed object context commits all current changes to the context's parent store. extension NSManagedObjectContext { /// Only performs a save if� A save is often followed by UI updates and multiple saves after each other could easily result in unnecessary reloads. Besides that, take into account that saved changes in a background context are merged into the view context, blocking the main queue shortly as well.


40+ CoreData Interview Questions And Answers ., One wrong answer during your first interviews can determine if you get the job, Changes to managed objects are held in memory, in the associated context, until that context is saved to one or more persistent stores. This is because NSManagedObjectContext and NSManagedObject, the two most� Overview. Use persistent history tracking to determine what changes have occurred in the store, and to update your view context only as needed. For example, consider an app that sometimes shows a list of colors, and sometimes shows a list of shapes.