Post

Replies

Boosts

Views

Activity

NSPersistentCloudKitContainer stop sync when CKErrorDomain 12 error occurred
My app uses NSPersistentCloudKitContainer to implement Coredata and iCloud synchronization data Recently, I received feedback from online users about data synchronization errors, and the error code is CKErrorDomain 12 . In the App, I use NSPersistentCloudKitContainer.eventChangedNotification to monitor the synchronization status, the following is the specific code NotificationCenter.default.publisher(for: NSPersistentCloudKitContainer.eventChangedNotification) .sink(receiveValue: { notification in if let cloudEvent = notification.userInfo?[NSPersistentCloudKitContainer.eventNotificationUserInfoKey] as? NSPersistentCloudKitContainer.Event { let event = SyncEvent(from: cloudEvent) } }) .store(in: &disposables) When the user feedbacks that the data cannot be synchronized, it can be seen from the above code that an error occurred when CoreData + iCloud was exporting data. At the same time, cloudKitEvent.error does not contain any error information, only CKErrorDomain 12 this information, I don’t know how to troubleshoot the error at all. What's even more frightening is that when the CKErrorDomain 12 error occurs, the app's synchronization service will stop immediately. Trying to restart the app or restart the phone, and turn off iCloud synchronization in the system, will not make the synchronization service work again. Only uninstalling and reinstalling can completely solve this problem, but users will also lose some data after uninstalling, because when the error occurs, any data generated by the user has not been successfully synchronized to iCloud, so these data will be lost after uninstalling. The following is the code for CoreDataStack initialization private func setupContainer(allowCloudKitSync: Bool) -> NSPersistentCloudKitContainer { let container = NSPersistentCloudKitContainer(name: containerName) let privateStoreURL = containerURL.appendingPathComponent(privateStoreName) let privateDescription = NSPersistentStoreDescription(url: privateStoreURL) let privateOpt = NSPersistentCloudKitContainerOptions(containerIdentifier: identifier) privateDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) privateDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) "allowCloudKitSync = \(allowCloudKitSync)".debugLog("CoreDataStack") if allowCloudKitSync { privateDescription.cloudKitContainerOptions = privateOpt } else { privateDescription.cloudKitContainerOptions = nil } container.persistentStoreDescriptions = [privateDescription] container.loadPersistentStores(completionHandler: { [weak self] (storeDescription, error) in if let error = error as NSError? { self?.dbStatePublish.send(.loadError) #if DEBUG "Unresolved error \(error), \(error.userInfo)".debugLog("CoreDataStack") #endif } else { if storeDescription.cloudKitContainerOptions == nil { self?.coreDataState = .local } else { self?.coreDataState = .cloud } "load coredata status = \(String(describing: self?.coreDataState))".debugLog("CoreDataStack") } }) let options = NSPersistentCloudKitContainerSchemaInitializationOptions() try? container.initializeCloudKitSchema(options: options) container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy container.viewContext.transactionAuthor = appTransactionAuthorName container.viewContext.automaticallyMergesChangesFromParent = true do { try container.viewContext.setQueryGenerationFrom(.current) } catch { fatalError("###\(#function): Failed to pin viewContext to the current generation:\(error)") } NotificationCenter.default.addObserver(self, selector: #selector(storeRemoteChange(_:)), name: .NSPersistentStoreRemoteChange, object: container.persistentStoreCoordinator) return container }
0
2
990
Apr ’23
CoreDataStack loadPersistentStores no completionHandler callback
In the past few weeks, about Coredata, I added the database backup function. After going online, I received feedback from users that the app opened with a white screen and could not be started normally. By investigating the reason why the App cannot be started, it is that there is no callback after loadPersistentStores is executed. This problem has never been reported by users before, until I added the code for data backup, so I suspect it has something to do with data backup. Below is the code for data backup func backupPersistentStore(atIndex index: Int, fileName: String) throws -> TemporaryFile { precondition(persistentStores.indices.contains(index), "Index \(index) doesn't exist in persistentStores array") let sourceStore = persistentStores[index] let backupCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel) let intermediateStoreOptions = (sourceStore.options ?? [:]) .merging([NSReadOnlyPersistentStoreOption: true], uniquingKeysWith: { $1 }) let intermediateStore = try backupCoordinator.addPersistentStore( ofType: sourceStore.type, configurationName: sourceStore.configurationName, at: sourceStore.url, options: intermediateStoreOptions ) let backupStoreOptions: [AnyHashable: Any] = [ NSReadOnlyPersistentStoreOption: true, NSSQLitePragmasOption: ["journal_mode": "DELETE"], // Minimize file size NSSQLiteManualVacuumOption: true, ] let backupFilename = fileName + ".sqlite" let backupFile = try TemporaryFile(creatingTempDirectoryForFilename: backupFilename) try backupCoordinator.migratePersistentStore(intermediateStore, to: backupFile.fileURL, options: backupStoreOptions, withType: NSSQLiteStoreType) return backupFile } When to back up sceneDidEnterBackground use Operation with UIApplication.shared.beginBackgroundTask class BackUpOpeartion: Operation {       override func main() {     LocalBackupManager.shared.backupSync()   }       static func canHandler() -> Bool {     let backupStrategy = LocalUserDefaults.standard.backupUiModel     let lastBackUpDate = LocalUserDefaults.standard.lastBackUpDate           let now = Date()     let sixHour: TimeInterval           #if DEBUG     sixHour = TimeInterval(0.15 * 60 * 60)     #else     sixHour = TimeInterval(backupStrategy.backupFrequency) // 6h     #endif           let backupDate = lastBackUpDate + sixHour           // Clean the database at most once per week.     guard now > backupDate else {       "It's not time to backup".debugLog()       return false     }           return true   }       static func doWhenEnterBackground() {     if !canHandler() {       return     }           var bgtToken = UIBackgroundTaskIdentifier.invalid     bgtToken = UIApplication.shared.beginBackgroundTask(expirationHandler: {       UIApplication.shared.endBackgroundTask(bgtToken)     })           guard bgtToken != .invalid else{       return     }           DispatchQueue.global(qos: .utility).async {       LocalBackupManager.shared.backupSync { succeed in         DispatchQueue.main.async {           if succeed {             LocalUserDefaults.standard.lastBackUpDate = Date()           }           UIApplication.shared.endBackgroundTask(bgtToken)         }       }     }   }     } BGProcessingTask in AppDelegate func registerBackgroundTasks() {     BGTaskScheduler.shared.register(forTaskWithIdentifier: dbBackupId, using: nil) { task in       self.backupDb(task: task as! BGProcessingTask)     }   } func scheduleDbBackup() {     if !BackUpOpeartion.canHandler() {       return     }           let request = BGProcessingTaskRequest(identifier: dbBackupId)     request.requiresNetworkConnectivity = false     request.requiresExternalPower = false           do {       try BGTaskScheduler.shared.submit(request)       "submit DbBackup".debugLog()     } catch {       print("Could not schedule app refresh: \(error)")     }   } func backupDb(task: BGProcessingTask) {           let queue = OperationQueue()     queue.maxConcurrentOperationCount = 1           let operation = BackUpOpeartion()           task.expirationHandler = {       queue.cancelAllOperations()     }           operation.completionBlock = {       let success = !operation.isCancelled       if success {         LocalUserDefaults.standard.lastBackUpDate = Date()       }       task.setTaskCompleted(success: success)     }   } Since before the online backup, there is no feedback about the white screen of the app failing to start, so I feel it is related to the backup I don't know under what circumstances it would cause CoreDataStack loadPersistentStores no completionHandler callback can someone help me? Thanks!
0
0
766
Nov ’22
SwiftUI PageTabView in iOS14.2 will Recall ChildView onAppear method Many times
I use TabView PageTabViewStyle with SwiftUI to display a pageview, when I swipe this TabView I find child view will Recall onAppear method Many times, Can someone tell me why? This is my code import SwiftUI struct Pageview: View { 		 		@StateObject var vm = PageViewModel() 		 		var body: some View { 				VStack { 						 						DragViewBar().padding(.top, 14) 						 						TabView(selection: $vm.selectTabIndex) { 								 								TextView(index: "0").tag(0) 								TextView(index: "1").tag(1) 								TextView(index: "2").tag(2) 								TextView(index: "3").tag(3) 								TextView(index: "4").tag(4) 								TextView(index: "5").tag(5) 								 						} 						.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) 						 				} 		} } struct TextView: View { 		 		let index: String 		 		var body: some View { 				VStack { 						Text(index) 				} 				.onAppear { print(index) } 				 		} } struct DragViewBar: View { 		var body: some View { 				Rectangle() 						.frame(width:36.0,height:5.0).foregroundColor(Color.blue) 						.cornerRadius(100) 		} } class PageViewModel: ObservableObject { 		@Published var selectTabIndex = 0 } When I swipe this Tabview, The result of the console printing 0 0 0 0 0 0 0 0 0 0 0 1 2 1 1 1 1 1 1 2 0 0 0 0 0 0 2 1 1 1 1 1 1 2 The correct case is to print only once per swipe It just has a problem in ios14.2, 14.1 will be ok, you can load my code in Github: https://github.com/werbhelius/TabViewBug Xcode version: 12.1 (12A7403) Device: iPhone 6s iOS 14.2 I think you can reproduce this problem on any device in iOS 14.2 I look forward to your help to solve this problem. Thank you
2
0
1.5k
Nov ’20