0

I would like to use Ensembles Framework to synch my core data with Swift.

Ensembles Framework

But I have some difficulties..

I try to do it like that: (Similar way to the example on github) I use a button to launch the tasks:

class ReglagesVC: UIViewController,UITableViewDataSource,UITableViewDelegate,UITextFieldDelegate,CDEPersistentStoreEnsembleDelegate {

 @IBAction func IcloudSynch(_ sender: UIButton) {

        CDESetCurrentLoggingLevel(CDELoggingLevel.verbose.rawValue)

        // Setup Core Data Stack
        self.setupCoreData()       

        // Setup Ensemble
        let modelURL = Bundle.main.url(forResource: "Mes_Vide_os", withExtension: "momd")
        cloudFileSystem = CDEICloudFileSystem(ubiquityContainerIdentifier: nil)
        ensemble = CDEPersistentStoreEnsemble(ensembleIdentifier: "Mes_Vide_os", persistentStore: storeURL, managedObjectModelURL: modelURL!, cloudFileSystem: cloudFileSystem)
        ensemble.delegate = self

        // Listen for local saves, and trigger merges
        NotificationCenter.default.addObserver(self, selector:#selector(localSaveOccurred(_:)), name:NSNotification.Name.CDEMonitoredManagedObjectContextDidSave, object:nil)
        NotificationCenter.default.addObserver(self, selector:#selector(cloudDataDidDownload(_:)), name:NSNotification.Name.CDEICloudFileSystemDidDownloadFiles, object:nil)

        // Sync
        self.sync(nil)  
    }


    //ENSEMBLES

    // MARK: Notification Handlers

    func localSaveOccurred(_ notif: Notification) {
        self.sync(nil)
    }

    func cloudDataDidDownload(_ notif: Notification) {
        self.sync(nil)
    }

    let appDelegate = UIApplication.shared.delegate as! AppDelegate



    var storeDirectoryURL: URL {
        return try! FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
    }

    var storeURL: URL {
        return self.storeDirectoryURL.appendingPathComponent("store.sqlite")
    }
    var managedObjectContext : NSManagedObjectContext!
    func setupCoreData() {
        let modelURL = Bundle.main.url(forResource: "Mes_Vide_os", withExtension: "momd")//"momd"
        let model = NSManagedObjectModel(contentsOf: modelURL!)

        try! FileManager.default.createDirectory(at: self.storeDirectoryURL, withIntermediateDirectories: true, attributes: nil)

        let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model!)
        let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]
        try! coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: self.storeURL, options: options)

        managedObjectContext = appDelegate.persistentContainer.viewContext
        managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
        managedObjectContext.persistentStoreCoordinator = coordinator
        managedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
    }


    // MARK: Ensembles

    var cloudFileSystem: CDECloudFileSystem!
    var ensemble: CDEPersistentStoreEnsemble!

    func sync(_ completion: (() -> Void)?) {
        //let viewController = self.window?.rootViewController as! ReglagesVC
        //self.activityIndicator?.startAnimating()

        if !ensemble.isLeeched {
            ensemble.leechPersistentStore {
                error in
                print("LEECH FINI___________________________")
                completion?()
            }
        }
        else {
            ensemble.merge {
                error in
                print("MERGE FINI___________________________")
                completion?()
            }
        }
    }

    func persistentStoreEnsemble(_ ensemble: CDEPersistentStoreEnsemble, didSaveMergeChangesWith notification: Notification) {

        managedObjectContext.performAndWait {
            self.managedObjectContext.mergeChanges(fromContextDidSave: notification)
        }
    }

    func persistentStoreEnsemble(_ ensemble: CDEPersistentStoreEnsemble!, globalIdentifiersForManagedObjects objects: [Any]!) -> [Any]! {
        let Films = objects as! [BaseFilms]
        print("films map")
        return Films.map { $0.id }
    }

}

And when I login my cloudkit dashboard I see that a container with has been created but there isn't any recors in the datas folder.

And nothing is synchonised between my devices.

Can you tell me where I am wrong??

Thank you.

Fabien
  • 1
  • 1

2 Answers2

0

By putting the setup code in an action, my guess is you are creating a new Ensemble (and Core Data stack) every time you press the button. You should setup the stack and ensemble once, perhaps on launch in viewDidLoad, and keep it in a property.

Note that the first time you call sync, it will "leech". This involves importing your local data, but it does not upload anything. The second time you call sync, it will download data from the cloud and upload. So you need to call sync twice — with the same ensemble object — before you will see any data in the CloudKit web interface.

Note also that you can only see data in CloudKit from the logged in user. So you still won't see anything unless you login there with the account you are testing with.

Drew McCormack
  • 3,490
  • 1
  • 19
  • 23
  • Thank you Drew, but I have place the code in the Appdelegate and done the Sync twice, and no change.And another question:I have 5 entities, so how should I setup the “persistentStoreEnsemble” function? I have write it for the first entitie (BaseFilms) but how to write it for all the entities? And when this function is called? func persistentStoreEnsemble(_ ensemble: CDEPersistentStoreEnsemble!, globalIdentifiersForManagedObjects objects: [Any]!) -> [Any]! { let Films = objects as! [BaseFilms] return Films.map { $0.id } } Does the sync problem come from this? – Fabien Oct 18 '17 at 07:35
  • And for the moment I only execute my app from 2 simulators. On each simulators I have setup the same Icloud login (The one of my developer account). Thank you in advance for your help. – Fabien Oct 18 '17 at 07:38
  • I recommend taking a look at the SimpleSync example. It is the simplest working use of Ensembles. I still think you have a problem putting the setup code in an action. Every time you press the button to sync, you are creating a new Ensemble. You don't want that. You do also need to setup the global identifier method properly. Every entity needs to have an `id`. You will either need to put in a big switch to choose between them, or just use `value(forKey:)` from Cocoa to get the `id`. – Drew McCormack Oct 18 '17 at 14:43
  • Thanks. I have change the location of the code now it's in my AppDelegate, and implemented the global identifier method, and I have seen some progress. I have now some errors ("Error Domain=NSCocoaErrorDomain Code=259 \....) I think that it's the same issues that this post : [link](https://github.com/drewmccormack/ensembles/issues/259). – Fabien Oct 19 '17 at 12:06
0

To solve my issues:

  1. Put the code in the correct place (in the AppDelegate).

  2. Install the latest update of Ensembles (1.7.1 instead of 1.7).

And that's all! Thank you Drew.

Just one thing still strange: When I add an object in one device it is synchronised on the other but when I delete an object it's not deleted on the other device and created again on the first device.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Fabien
  • 1
  • 1