-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Creating data in public database does not sync with other devices #19
Comments
Just an additional note (issue): deleting the app and reinstalling on either device does not fetch any of the data that was created previously on the same device. |
Adding the project that I'm using in case there are any questions about what I've modified. Notice entity changes and bundle ID. I'm enabling world access and everyone security roles for now if you can test. Thanks in advance! |
I suspect it has something to do with self.tokens.tokensByDatabaseScope[database.databaseScope.rawValue] Since the app is reinstalled or installed to a new device, there are no change tokens present? Is there the need to handle change token changes? |
Use the class PublicDatabaseSubscriptions to sync public record types. Also, as I look thru my own app's code, I see that I'm doing a hard sync the first launch to grab all the existing records. That could move into CloudCore itself, but would need to be generalized. Here's my code… `
` |
@deeje thanks for the quick response. I got it working with the code that you suggested. Now I have one further blocker that I think might be in your court and could just be my misunderstanding of how the app is handling persistent change tokens or history. I ran the app on the simulator and added some data. I then ran the app on my physical device and made sure that both apps had the same data while running. The simulator is still running the app from the first launch: You'll notice that the first 4 rows have five employees. I added these manually while my physical device was also running the app to ensure that the changes were coming through, and they did. Both the simulator and the physical device were matched up with employee counts. I made sure to only add employees in the simulator to ensure that notifications were received successfully on the physical device. I then killed the app on my physical device and deleted it to test data coming in from a fresh install. After installing the app again, the data on my physical device no longer reflects the changes and I'm not exactly sure where the ∆ arrises. I've encountered this behavior several times now, and I'm wondering if there is anything else I need to set for the app to remain in sync even after a fresh install. I've included my AppDelegate here for your reference. Thanks again in advance! //
// AppDelegate.swift
// CloudTest2
//
// Created by Vasily Ulianov on 14.02.17.
// Copyright © 2017 Vasily Ulianov. All rights reserved.
//
import UIKit
import CoreData
import CloudCore
import Reachability
import CloudKit
let persistentContainer = (UIApplication.shared.delegate as! AppDelegate).persistentContainer
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
let delegateHandler = CloudCoreDelegateHandler()
var reachability: Reachability?
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// Register for push notifications about changes
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (status, error) in
if let _ = error {
}else {
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
}
// Enable uploading changed local data to CoreData
CloudCore.delegate = delegateHandler
CloudCore.enable(persistentContainer: persistentContainer)
NotificationCenter.default.addObserver(self,
selector: #selector(reachabilityChanged(notification:)),
name: .reachabilityChanged,
object: reachability)
reachability = try! Reachability(hostname: "icloud.com")
try? reachability?.startNotifier()
pullPublic(entityType: "Organization")
pullPublic(entityType: "Employee")
return true
}
@objc private func reachabilityChanged(notification: Notification) {
let reachability = notification.object as! Reachability
CloudCore.isOnline = reachability.connection != .unavailable
}
// Notification from CloudKit about changes in remote database
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// Check if it CloudKit's and CloudCore notification
if CloudCore.isCloudCoreNotification(withUserInfo: userInfo) {
// Fetch changed data from iCloud
CloudCore.pull(using: userInfo, to: persistentContainer, error: {
print("fetchAndSave from didReceiveRemoteNotification error: \($0)")
}, completion: { (fetchResult) in
completionHandler(fetchResult.uiBackgroundFetchResult)
})
}
}
// MARK: - Default Apple initialization, you can skip that
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
// MARK: Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "Model")
if #available(iOS 11.0, *) {
let storeDescription = container.persistentStoreDescriptions.first
storeDescription?.setOption(true as NSNumber, forKey:NSPersistentHistoryTrackingKey)
if #available(iOS 13.0, *) {
storeDescription?.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
}
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
guard let error = error as NSError? else { return }
fatalError("###\(#function): Failed to load persistent stores:\(error)")
})
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
container.viewContext.undoManager = nil
container.viewContext.shouldDeleteInaccessibleFaults = true
container.viewContext.automaticallyMergesChangesFromParent = true
do {
try container.viewContext.setQueryGenerationFrom(.current)
} catch {
fatalError("###\(#function): Failed to pin viewContext to the current generation:\(error)")
}
return container
}()
func pullPublic(entityType: String, predicate: NSPredicate? = nil) {
let moc = persistentContainer.newBackgroundContext()
let queue = OperationQueue()
let query = CKQuery(recordType: entityType, predicate: predicate ?? NSPredicate(value: true))
let queryOp = CKQueryOperation(query: query)
queryOp.recordFetchedBlock = { record in
let convertOperation = RecordToCoreDataOperation(parentContext: moc, record: record)
queue.addOperation(convertOperation)
}
queryOp.queryCompletionBlock = { cursor, error in
queue.addOperation {
moc.perform {
try? moc.save()
}
}
}
CKContainer.default().publicCloudDatabase.add(queryOp)
}
// MARK: Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
} |
I should also note that CloudKit dashboard reflects the 5 employees for the first 4 companies, so the data is there, it's just not getting fetched from a fresh app install on the separate device. |
So far, I appear to be creating records successfully using the example app.
The first record was created using the simulator, and the second record created using my own physical device. However, it doesn't appear that the two instances of the app receive notifications for sync and so do not appear to be syncing at all. This is the boilerplate example app, which I've modified the entities to use public rather than private, and removed the organization parent as I've mentioned in a previous post.
Do you have any advice on what might be causing the two devices not to sync? In addition, I had a separate user in a separate geographic location use the same container. He is able to create records in the same public CloudKit container, but again, we do not see updates from each other's app instances.
Thanks in advance!
Device A:
Device B:
The text was updated successfully, but these errors were encountered: