drewmccormack / ensembles

A synchronization framework for Core Data.
MIT License
1.63k stars 131 forks source link

Container working, no data flowing between devices #215

Closed sumowesley closed 8 years ago

sumowesley commented 8 years ago

I have just started to use Ensembles for one of my projects. I now have an iCloud storage area for my product and I can see the correct directories in my iCloud container. I am using my Mac to watch the directories and do clear outs when I'm testing.

An iPad and an iPhone are using my app. If I start off with an empty container, both can perform the leech operation and create the Ensembles data structure. The second device never receives updates even though it leeches successfully.

If I close down the app, delete the Ensembles data and reinstall and reverse the devices, I still get the Ensembles files but the second does not get the updates.

Notes:

The global identifiers delegate is set using the below but based on putting a breakpoint in there, it is never called (delegate is set, see further below):

- (NSArray *)persistentStoreEnsemble:(CDEPersistentStoreEnsemble *)ensemble globalIdentifiersForManagedObjects:(NSArray *)objects
{
    return [objects valueForKeyPath:@"strIdent"];
}

The code in my Appdelegate.m file to manage the setup of Ensembles is:

NSManagedObjectModel *model = [NSManagedObjectModel MR_newManagedObjectModelNamed:@"MyApp.momd"];
    [NSManagedObjectModel MR_setDefaultManagedObjectModel:model];

    // Setup Core Data Stack
    [MagicalRecord setShouldAutoCreateManagedObjectModel:NO];
    [MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:kSTR_TAG_COREDATA_DBNAME];

    if (settings.syncService == SyncServiceiCloud) {
    // Setup Ensemble
        NSURL *url = [NSPersistentStore MR_urlForStoreName:[MagicalRecord defaultStoreName]];
        NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyApp" withExtension:@"momd"];
        cloudFileSystem = [[CDEICloudFileSystem alloc] initWithUbiquityContainerIdentifier:nil];
        ensemble = [[CDEPersistentStoreEnsemble alloc] initWithEnsembleIdentifier:@"MyApp.v1" persistentStoreURL:url managedObjectModelURL:modelURL cloudFileSystem:cloudFileSystem];
        ensemble.delegate = self;

        // Listen for local saves, and trigger merges
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(localSaveOccurred:) name:CDEMonitoredManagedObjectContextDidSaveNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cloudDataDidDownload:) name:CDEICloudFileSystemDidDownloadFilesNotification object:nil];

        [self syncWithCompletion:NULL];

    }

Any thoughts as to why the sync is not happening when I can create the Ensembles structure?

sumowesley commented 8 years ago

Neither of the selectors for the observers are being called. Directories show same number of files in each directory and that hasn't change since about the time the leech actions were performed.

sumowesley commented 8 years ago

Above not quite true - the cloudDataDidDownload fires a couple of times in the first session (the one where the device leeches) but subsequent load of same device doesn't seem to invoke this. Neither of the delegate methods are fired though.

drewmccormack commented 8 years ago

An iPad and an iPhone are being using my app. If I start off with an empty container, both can perform the leech operation and create the Ensembles data structure. The second device never receives updates even though it leeches successfully. Leeches don’t upload any data. The first merge you do after the leech should put data in iCloud. Are you merging some time after the leech? Notes:

I'm using Magical Record for my core data management There are some risks with Magical Record. It saves asynchronously in the background. See sections in book about it, and see MagicalRecord sample app.

Part of the startup sequence of the app is to populate with some initial reference data which is present on both devices as this is done outside of Ensembles. Should not be a problem, as long as you make sure you save it fully to disk before starting to sync. There are no Leech errors (breakpoint show it being called) No error in the completion block? There are no Merge errors (breakpoint shows it being called) No error in the completion block? The global identifiers delegate is set using the below but based on putting a breakpoint in there, it is never called (delegate is set, see further below):


{
return [objects valueForKeyPath:@"strIdent"];
}

This should definitely be called during leeching. If not, do you actually have a store on disk with objects in it when you leech? If the store is empty, this would not get called, though it should get called when you save some data and then do a merge.

The code in my Appdelegate.m file to manage the setup of Ensembles is:
```NSManagedObjectModel *model = [NSManagedObjectModel MR_newManagedObjectModelNamed:@"MyApp.momd"];
    [NSManagedObjectModel MR_setDefaultManagedObjectModel:model];

// Setup Core Data Stack [MagicalRecord setShouldAutoCreateManagedObjectModel:NO]; [MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:kSTR_TAG_COREDATA_DBNAME];

if (settings.syncService == SyncServiceiCloud) { // Setup Ensemble NSURL url = [NSPersistentStore MR_urlForStoreName:[MagicalRecord defaultStoreName]]; NSURL modelURL = [[NSBundle mainBundle] URLForResource:@"MyApp" withExtension:@"momd"]; cloudFileSystem = [[CDEICloudFileSystem alloc] initWithUbiquityContainerIdentifier:nil]; ensemble = [[CDEPersistentStoreEnsemble alloc] initWithEnsembleIdentifier:@"MyApp.v1" persistentStoreURL:url managedObjectModelURL:modelURL cloudFileSystem:cloudFileSystem]; ensemble.delegate = self;

// Listen for local saves, and trigger merges
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(localSaveOccurred:) name:CDEMonitoredManagedObjectContextDidSaveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cloudDataDidDownload:) name:CDEICloudFileSystemDidDownloadFilesNotification object:nil];

[self syncWithCompletion:NULL];

}



Any thoughts as to why the sync is not happening when I can create the Ensembles structure?

What other delegate methods do you have? You at least need the did-save one, and use that to update your contexts. See quick start guide and the Magical Record example app.

Drew

sumowesley commented 8 years ago

The merge element is being run as part of that syncWithCompletion method and breakpoints show is being run.

Initial data is written to disk as part of the load routine using

[self.managedObjectContext  MR_saveToPersistentStoreAndWait];

There is no error in the completion blocks for both leech and merge.

I definitely have data in the local store as I'm seeing them in my view controllers and I add new ones interactively. By the time I have turned on iCloud sync, there is data in there.

I do have a save delegate which is never called:

- (void)persistentStoreEnsemble:(CDEPersistentStoreEnsemble *)ensemble didSaveMergeChangesWithNotification:(NSNotification *)notification
{
    NSManagedObjectContext *rootContext = [NSManagedObjectContext MR_rootSavingContext];
    [rootContext performBlockAndWait:^{
        [rootContext mergeChangesFromContextDidSaveNotification:notification];
    }];

    NSManagedObjectContext *mainContext = [NSManagedObjectContext MR_defaultContext];
    [mainContext performBlockAndWait:^{
        [mainContext mergeChangesFromContextDidSaveNotification:notification];
    }];
}

It feels like there is an initial connect to iCloud to do some setup but that no data is being exchanged. I leeched both devices earlier today. Subsequent connects are not making any updates in the iCloud container even though I have added new managed objects.

drewmccormack commented 8 years ago

I definitely have data in the local store as I'm seeing them in my view controllers and I add new ones interactively. By the time I have turned on iCloud sync, there is data in there. And does the global-identifier delegate method get called? If not, something is definitely wrong. Double check the name of the method. It should be called when leeching if there is data in the store. It feels like there is an initial connect to iCloud to do some setup but that o data is being exchanged. I leeched both devices earlier today. Subsequent connects are not making any updates in the iCloud container even though I have added new managed objects.

Do you see new files appear in the events folder of your cloud container on the Mac? Do all devices have iCloud Drive turned on for your app? Perhaps you can even list the contents of your iCloud container on the iPhone (eg using NSFileManager) to see if there is data in there. You can also turn on verbose logging for Ensembles. The logs may tell us what is going wrong.

Drew

sumowesley commented 8 years ago

I have breakpoints in both delegate methods when running through Xcode and neither method is called.

There are two events only in the version I can see of the container on the Mac - one was at 7:31 and one was at 12:07.

iCloud turned on for the app on both devices I'm using.

Have attached log from iPad - covers loading the app and adding of a new record.

ensemble_log_160110-1232.txt

sumowesley commented 8 years ago

Progress. Seems to be the setup of the stack - can't do the explicit Sqlite call shown in the comment below. Have to use the bare core data stack.

    [MagicalRecord setupAutoMigratingCoreDataStack];
    //[MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:kSTR_TAG_COREDATA_DBNAME];

Now have both delegates showing as fired.

sumowesley commented 8 years ago

Sorry Drew, meant to offer my thanks for you reading through my messages. It is much appreciated.

drewmccormack commented 8 years ago

Glad it is sorted out.

Kind regards, Drew

On 10 Jan 2016, at 14:04, sumowesley notifications@github.com wrote:

Sorry Drew, meant to offer my thanks for you reading through my messages. It is much appreciated.

— Reply to this email directly or view it on GitHub.