Relational Integrity and Validation in Core Data iCloud Syncing

▼魔方 西西 提交于 2019-12-20 10:27:00

问题


Consider the following simple entity model: Entity A has a to-one relationship to Entity B called b. Entity B has an inverse to-one relationship called a. Neither relationship is optional.

A            B
b  < ----- > a

Assume we have two devices (1) and (2) that begin fully synced. Each has one object of class A, and one object of class B, and they are associated with one another. On device 1, we have objects A1 and B1, and on device B we have the same logical objects A1 and B1.

Now assume that simulateous changes are made on each device:

On device 1, we delete B1, insert B2, and associate A1 with B2. Then save changes.
On device 2, we delete B1, insert B3, and associate A1 with B3. Then save changes.

Device 1 now attempts to import the transaction logs from device 2. B3 will be inserted, and A1 will be associated with B3. So far so good, but B2 is now left with relationship a equal to nil. The a relationship is non-optional, so a validation error occurs.

Something similar will occur on device 2, because there are two B objects, and only one A object to be associated with. There must thus always be a validation error, because one of B objects must have an a relationship set to nil.

Even worse, any future change will always leave an errant B object hanging around, and so will fail validation. In effect, the user cannot fix the problem themselves by resetting the relationship. It is permanently broken.

The question is, how can you address a validation error like this? This all happens before the NSPersistentStoreDidImportUbiquitousContentChangesNotification notification is triggered. It is not a validation error in your apps main NSManagedObjectContext, it is a validation error that occurs during the initial import of transaction logs into the persistent store.

The only option I can think of is perhaps to try to delete the invalid B object in a custom setter (setA:), or in a KVC validation method (validateA:error:) on the B class itself, because these do seem to be triggered during the transaction log import. But I'm not sure that side effects like these are allowed, and when I try it, it does seem to result in nasty log messages to that effect.

Anyone know what the right way to handle this is?


回答1:


Check out this thread on Apple's Developer Forums:

https://devforums.apple.com/message/641930#641930

There is a response from an Apple employee. In a nutshell:

1) This is a known bug in Core Data iCloud syncing under current versions of iOS (5.1) and OS X (10.7.3).

2) If the relationship is non-optional with a validation predicate, syncing will cease completely. So you will need to remove the validation for the time-being to keep things flowing. However, doing so will leave the devices with mismatched data.

3) There is no official workaround for this. One approach, which is messy, would be to maintain a separate attribute which tracks the relationship. Then, you would need to scan through any objects changed via iCloud and fix the relationship if it is nil.

I am also bitten by this bug. Very frustrating to deal with customers who run into troubles. Hopefully a fix from Apple with be out soon...




回答2:


In case this helps others, I have made a bit more progress working around this bug in iCloud. What I've done is disabled validation during the initial transaction log import, and enabled it again when my MOC goes to save to my main store. I still get validation errors, of course, but they happen in my MOC save method, rather than deep in the Core Data framework, so I can repair the errors (e.g. delete invalid objects).

How do I 'disable' validation? The way I am doing it is to override the KVC validation method in my NSManagedObject subclass, which I have used as the root class for all Core Data entity classes.

-(BOOL)validateValue:(__autoreleasing id *)value forKey:(NSString *)key error:(NSError *__autoreleasing *)error 
{
    if ( ![self.managedObjectContext isKindOfClass:[MCManagedObjectContext class]] ) {
        return YES;
    }
    else {
        return [super validateValue:value forKey:key error:error];
    }
}

In the override, I check the class of the managed object context, and if it is my custom class, I do the validation normally by chaining to the super method. If it is not my custom subclass, I return YES to indicate validity.

Of course, you could make this more nuanced, but you hopefully get the idea: you try to determine whether this is one of your app's standard saves or a iCloud import save, and branch accordingly.



来源:https://stackoverflow.com/questions/10194023/relational-integrity-and-validation-in-core-data-icloud-syncing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!