Relational Integrity and Validation in iCloud Syncing Master Data

Consider the following simple entity model: Entity A has a to-one relationship with Entity B called b. Entity B has an inverse relation to one called a. None of these are required.

AB b < ----- > a 

Suppose we have two devices (1) and (2) that start to fully synchronize. Each of them has one object of class A and one object of class B, and they are connected to each other. On device 1, we have objects A1 and B1, and on device B we have the same logical objects A1 and B1.

Now suppose that on each device simulated changes are made:

On device 1, we remove B1, insert B2, and match A1 to B2. then save the changes.
On device 2, we remove B1, insert B3, and match A1 to B3. Then save the changes.

Device 1 is now trying to import transaction logs from device 2. B3 will be inserted, and A1 will be associated with B3. So far so good, but now B2 remains with a ratio of zero. The relationship a is not optional, so a validation error occurs.

Something similar will happen on device 2, because there are two objects B and only one object A with which you need to associate. Thus, there should always be a validation error, since one of the B objects must have a relation set to nil.

Worse, any future changes always leave the stray object B hanging around, and therefore will refuse to check. In fact, the user cannot solve the problem on his own, dropping the relationship. He is constantly disturbed.

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

The only possibility I can think of is perhaps to try to delete the invalid B object in the user setter ( setA: or in the KVC validation method ( validateA:error: in class B itself, since they seem to work during import transaction log. But I'm not sure that such side effects are resolved, and when I try to do this, it seems that this leads to unpleasant log messages on this subject.

Does anyone know which is the right way to handle this?

+6
source share
2 answers

Check out this thread on the Apple 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 the synchronization of Core Data iCloud in current versions of iOS (5.1) and OS X (10.7.3).

2) If the link is not optional with the check predicate, synchronization will completely stop. Thus, you will need to remove the validation so that time goes on. However, this will result in devices having inappropriate data.

3) There is no official workaround for this. One approach that is messy would be to maintain a separate attribute that tracks the relationship. Then you will need to scan any objects modified using iCloud and fix the relationship if it is zero.

I am also bitten by this mistake. It is very unpleasant to deal with customers who are facing problems. Hope Apple's fix disappears soon ...

+2
source

If this helps others, I improved the work on this error in iCloud a bit. What I did was turn off verification during the initial import of the transaction log and turn it back on when my MOC goes to save to my main store. Of course, I still get validation errors, but they occur in my method of saving the MOC, and not in the back of the Core Data structure, so I can fix the errors (for example, delete invalid objects).

How to disable verification? The way I do this is to override the KVC validation method in my NSManagedObject subclass, which I 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 overriding, I check the context class of the managed object, and if this is my custom class, I usually check it by binding to the super method. If this is not my custom subclass, I return YES to indicate validity.

Of course, you can make it more subtle, but I hope you get this idea: you are trying to determine if this is one of the standard saves of your application or saving iCloud imports, and the branch accordingly.

+1
source

Source: https://habr.com/ru/post/913436/


All Articles