简体   繁体   中英

Get modification date for NSManagedObject in Core Data?

Outside of adding an NSDate property to each Entity in my Core Data store, is there a programmatic way to get the modification date for any object?

No, you must add a date and manage it yourself. You can use override -willSave in your managed object to update the time stamp, but read the API documentation for NSManagedObject on -willSave for how to update without causing a willSave loop (the docs even talk about the case of updating a timestamp). The docs also mention using the NSManagedObjectContextWillSaveNotification , but that may be more trouble to set up than a simple check to not set the timestamp too quickly.

I personally check if updatedAt was modified, and if yes then I don't touch it anymore. This way I break the willSave loop.

- (void)awakeFromInsert {
    [super awakeFromInsert];

    self.primitiveUpdatedAt = [NSDate date];
}

- (void)willSave {
    [super willSave];

    if(![self isDeleted] && self.changedValues[@"updatedAt"] == nil) {
        self.updatedAt = [NSDate date];
    }
}

Please note this solution assume we have a property called dateUpated in model.

Instead of handling this at individual objects. I would handle this through a notification. Apple documentation also suggests this way as well.

1. Register for NSManagedObjectContextWillSaveNotification notification

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(willSaveContext:)         
                                             name:NSManagedObjectContextWillSaveNotification 
                                           object:nil];

2 Set the property for updatedDate at observer method for each updated object.

- (void)willSaveContext:(NSNotification *)notification{

    NSManagedObjectContext *context = [notification object];
    NSSet *updatedObject = [context updatedObjects];

    for (NSManagedObject *managedObject in [updatedObject allObjects]) {
        if ([[managedObject.entity propertiesByName] objectForKey:@"dateUpdated"]) {
            [managedObject setValue:[NSDate date] forKey:@"dateUpdated"];
        }

    }

}

At WWDC19 it was announced Derived Attributes .

An updated attribute like this one is updated automatically by Core Data:

核心派生属性截图

Note: this may produce an invalid Mapping Model .

I found this Q/A helpful in getting started on setting up an attribute for date modified in Core Data. In the process, I arrived at a couple of tips that might also help:

(Tip 1: avoiding willSave recursion)

  • Another way to avoid a willSave loop is to write a custom routine for saving the context that iterates through its updatedObjects looking for those that have a dateModified property and setting it. Do the actual calls to commitEditing and save after that loop. Don't bother with willSave at all.

(Tip 2: live updating — but won't work with undo)

  • If you need live updating to display the date modified to the user in a table, you can also set the dateModified in your switch-case (or whatever) for the date-modified column in your objectValueForTableColumn delegate method (willDisplayCell for iOS).

  • There, test if the object for that row isUpdated and, if so, set the dateModified. I doubt that the if (obj isUpdated) check is very expensive. But be sure to do this only for the relevant column so as not to repeat it unnecessarily.

  • Return whatever string representation you are using for the column. To avoid obvious disparities between the time of the actual modification and the date set, show only the date, not the time.

  • This will cause dateModified to be updated whenever the user makes a table selection, as well as when the table is reloaded. Which is not perfect -- if user modifies an attribute that is not represented in the table, the column won't update until they make a selection. But it's reasonably responsive and a lot easier than implementing an extensive KVO scheme.

  • (You will still want to set the date in the save routine, to catch modifications which are invisible to the table.)

  • UNDO COMPLICATION: Unfortunately, the undo manager will consider the table delegate method's change to dateModifed as an event unto itself. Subsequently calling undo simply undoes the last change to dateModified — over and over again. I tried overcoming this by adding a tracker and a check for a non-empty changedValues dictionary to ensure that the dateModified got set only once pre-save. That worked for undoing deletions, but not for regular edits. So there's no quick way of accomplishing live updating of dateModified pre-save.

If anyone wants an easy way of doing this in Swift: I wrote a blog post about a simple UpdateListener class to update all the updateDate and insertDate properties. You can find the entire class in this gist . All you have to do is call UpdateListener.setupSharedInstance() in the application(:didFinishLaunchingWithOptions:) method of your application delegate.

A Swift version of Ben Affleck's answer

public override func willSave() {
    super.willSave()
        
    if !isDeleted, changedValues()["updatedAt"] == nil {
        self.setValue(Date(), forKey: "updatedAt")
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM