8

So...I have an app with StoreData and iCloud enabled. My data is syncing between the devices but I don't ge the behaviour I want when it comes to reflecting the changes in the UI.

The behaviour I want: When a user has my app open on two devices (A & B) and makes a change one one (A) of them I want the change to automatically reflect in the second (B) device UI within some reasonable time.

enter image description here

The behaviour I currently have: When the user makes changes on one device (A) nothing ever happens on the other (B)...until I minimise and maximise the app on the second device (B) (Not restarting...just minimising).

enter image description here

My thought are that the app won't receive the changes (iCloud won't sync) until some action is triggered when minimising and reopening the app.

But I don't know. According to Syncing a Core Data Store with CloudKit syncing and notifying other devices should happened automatically. And I think my View should be hooked up correctly as it will change instantly when making local changes to data in the CoreData database.

My SwiftUI view pretty much looks like this:

import SwiftUI
import CloudKit
import Foundation

struct MyView: View {
    @FetchRequest(fetchRequest: Entity.fetchRequest() as! NSFetchRequest<Entity>) var entities: FetchedResults<Entity>

    var body: some View {
        List {
            ForEach(entities, id: \.id) { e in
                Text("\(e.title)")
            }
        }
    }
}

public class Entity: NSManagedObject {
    @NSManaged public var id: UUID?
    @NSManaged public var title: String
}

Any Help is much appreciated! (Let me know if I need to provide additional information.)

iSebbeYT
  • 1,441
  • 2
  • 18
  • 29
  • Check this answer https://stackoverflow.com/questions/58860967/core-data-cloudkit-not-refreshing-on-other-device-automatically – Alberto Lunardini Mar 31 '20 at 21:09
  • 1
    @AlbertoLunardini sadly I already have "context.automaticallyMergesChangesFromParent = true" and It does not make any difference. – iSebbeYT Apr 05 '20 at 18:53
  • Do you by any chance use a simulator and a real device to test? I have found that the simulator does not update when you make a change on the real device, but the real device will update when you make a change on the simulator (XCode 11.4). – Ch Ryder Apr 06 '20 at 10:34
  • 1
    @Charles I am using an iPhoneX and an iPad Pro. Both running 13.4 – iSebbeYT Apr 06 '20 at 10:49
  • I see. And have you also tried `try? context.setQueryGenerationFrom(.current)`. For every time you access the context, not just once? – Ch Ryder Apr 06 '20 at 11:02
  • @Charles to my understanding `try? context.setQueryGenerationFrom(.current)` should only be used to pin some current state of CoreData to prevent the view from loading CoreData changes (source: [Docs](https://developer.apple.com/documentation/coredata/accessing_data_when_the_store_has_changed)). Currently I don't use `try? context.setQueryGenerationFrom(.current)` anywhere in my code. – iSebbeYT Apr 06 '20 at 11:26
  • @Charles actually, I have done some more basic testing and I think it all has to do with iCloud not syncing until I minimise the app. Because if I do: 1. perform a change on one device and then don't touch them for a couple minutes (enough time for iCloud to be able to sync) 2. After waiting: Put the other device in flight mode, then minimise the app and reopen. Then no changes will be performed at all until I turn off flight mode. Though even with this knowledge I'm still not sure how I should go on to achieve the behaviour I want. – iSebbeYT Apr 06 '20 at 11:49
  • It looks like the UI is not reacting to a change of history, which `context.setQueryGenerationFrom(.current)` might trigger. I empathise with your problem, I had almost exactly the same last week, and I pulled my hair out for days. My issue was that I did not modify the `viewcontext` in one single but crucial case. – Ch Ryder Apr 06 '20 at 12:48
  • Does it work now? I have the same problem! – Kai Zheng Sep 26 '20 at 07:56

1 Answers1

2
// MARK: - Core Data stack

lazy var persistentContainer: NSPersistentCloudKitContainer = {

    let container = NSPersistentCloudKitContainer(name: "ComCloudSync")
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()

Try adding the following code (change "ComCloudSync" to your appName):

// MARK: - Core Data stack

lazy var persistentContainer: NSPersistentCloudKitContainer = {

    let container = NSPersistentCloudKitContainer(name: "ComCloudSync")
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })

    container.viewContext.automaticallyMergesChangesFromParent = true
    container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy

  return container
}()
Sir Spiff
  • 29
  • 5
  • thanks this helped! BUT why is it pointing only to the Private database? i tried `storeDescription.cloudKitContainerOptions?.databaseScope = .public` in my Presistence.swift file but this didnt work – Di Nerd Apps May 01 '21 at 22:16