iCloud + CoreData: ¿cómo evitar la duplicación de datos precargados?

I have a problem with an iCloud shoebox application and hope some-one can help me (I've spent many hours fighting it in vain).

La aplicación: - A simple library style application - containing set of categories (Cat1 .. CatN) each containing items (Item1...ItemM). I used Apple's iPhoneCoreDataRecipes to set up iCloud CoreData stack.

Everything works almost perfect with iCloud except - there should be a number of pre-filled empty categories the user can start using once he has opened the app for the first time (he can also be offline at that time). And here's the devil.

Here's what I do - Once my persistentStoreCoordinator is setup I send notification

dispatch_async(dispatch_get_main_queue(), ^{
    [[NSNotificationCenter defaultCenter]
        postNotificationName: @"RefetchAllDatabaseData"
                      object: self
                    userInfo: nil];
    });

which is received by my MasterViewController. When the notification is received MasterViewController checks the number of categories in the storage. If the number of available categories equals 0 - the pre-filled categories are inserted.

FYI - I use NSMergeByPropertyObjectTrumpMergePolicy for my ManagedObjectContext

El problema: This works well for the 1st device. But for the 2nd device the default categories from iCloud are a menudo received later than persistentStoreCoordinator has been setup (and default categories inserted by 2nd device). In the end I have 2 sets of categories with the same names on both devices.

¿Alguna idea de cómo se puede resolver esto?

Soluciones probadas: I tried 2 strategies to solve this. Both start in the same way. After I call

[moc mergeChangesFromContextDidSaveNotification: note];

Llamo

[self materializeKeysWithUserInfo: note.userInfo forContext: moc];

many thanks to Jose Ines Cantu Arrambide from https://devforums.apple.com/thread/126670?start=400&tstart=0 for his reference code - In essence

materializeKeysWithUserInfo:forContext:

get managedObjectIds from note.userInfo and retrieves corresponding objects from ManagedObjectContext putting them into a dictionary.

Estrategia 1:

  • All my categories have creation time-stamps.
  • On insert from iCloud, get pairs of categories with same name if any
  • Select older duplicate categories
  • move their items to newer duplicate categories
  • delete older duplicate categories

These strategy effectively removes duplicates on both devices even before they appear in the UI PERO

1) the items from 1st device are getting lost on the 2nd device - when they come to the 2nd device their parent category is absent and their category field equal nil so I don't know where to put them.

2) in some short time the items that got lost on the 2nd device are also getting lost on the first due to conflicts.

3) some items originating from the 2nd device are also lost due to conflicts.

I tried to prefer older categories against newer but it didn't give any effect

Estrategia 2:

  • All my categories have creation time-stamps.
  • All categories have obsoleto boolean field set to NO en la creación
  • On insert from iCloud, get pairs of categories with same name if any
  • Select older duplicate categories
  • move their items to newer duplicate categories
  • mark older categories with obsolete = YES

These strategy casi siempre removes duplicates on both devices even before they appear in the UI PERO

the majority (or all) of the items from both devices are getting lost due to a bunch of conflicts on categories and items.

Some concluding thoughts:

It looks like these strategy doesn't work as we start simultaneously changing content ob both devices whereas iCloud is not suitable for such pattern.

In my tests I had both devices running simultaneously. I cannot neglect a case when a happy user who has just bought his 2nd iDevice installs my app on the 2nd device (with tre 1st device running the app) and get lost all his items in the matter of minutes.

Any ideas how this situation can be solved? Do you think iCloud + CoreData is ready for production?

Estrategia 3

I've tried to put a pre-filled database (copying it from bundle) to the appropriate path. It worked out partly - I have no more pre-filled categories duplication PERO the items added to the pre-filled categories do not synchronize across the devices.

iCloud is not aware of the data that exists in the database prior to iCloud setup - my 2nd device receives items, inserted on the 1st device in pre-filled categories, with category = nil.

Items in additionally categories (as well as categories themselves) inserted into the storage after iCloud setup do synchronize properly.

preguntado el 01 de febrero de 12 a las 14:02

1 Respuestas

Strategy 1 with some modifications appeared to be a working solutions (with some flaws though).

Leyenda:

  • 1st device - started online without any content in the iCloud
  • 2nd device - started later than first and OFFLINE. Then it gets online after some items added

So here's the updated strategy:

  • All my categories have creation time-stamps

  • Las categorias podrá be renamed (only added or deleted - this is crucial)

  • All my items have a string categoryName field which gets its value upon item creation and updated whenever item is moved to a different category - this redundante information helps to achieve success;

On insertion of new Categorías:

  • On insert from iCloud, I get pairs of categories with same name if any

  • Seleccionar más nuevo duplicate categories (they will most probably have less items than old ones so we will have less dance in iCloud)

  • Move their items if any to mayor duplicate categories

  • Borrar más nuevo duplicate categories

On insertion of new Artículos - if the item belongs to borrado categoría:

  • CoreData tries to merge it and falla as there's no parent category any more (lots of errors in console). It promisses to insert it later.

  • After some short time it does merge and insert the item into storage but with NIL categoría

  • Here we pick our item up, find out it's parent category from categoryName and put it to the correct category

VOILA! - no duplicates & everybody happy

Un par de notas:

  1. I get a dance of items belonging to the 2nd device (those that will come with nil category to the 1st device) on both devices. After a couple of minutes everything is stabilized
  2. No items is lost though
  3. The dance happens only on the first iCloud sync of the 2nd (or any other subsequent device)
  4. If the 2nd device is started en línea. for the first time the chance that duplicate categories case appears is about 25% only - tested on 3G connection - so dance should not affect the majority of users

Respondido 03 Feb 12, 17:02

Konstiantyn, I have followed your conversations on the dev. site. I really appreciate you adding the question here - I wish there were more comments! Did this strategy work for you? Did you consider having a prebuilt localStore, as in the SharedCoreData sample? Regards - ICL1901

You can have your categories renamed, as long as you can distinguish between system-created records and user-created records. I used a pair of createDate y la updateDate atributos a determine whether a data item should be de-duplicated. - adib

Hi guys, I had to stop at some point 2 years ago and didn't revisit CoreData + iCloud since then. @adib - thanks for your comment. I would use this technique later if needed. - Kostiantyn Sokolinskyi

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.