iOS Swift 中 CoreData 的 iCloud Backup for .sqlite 数据库文件

iCloud Backup for .sqlite database file for CoreData in iOS Swift

提问人:Aniket Rao 提问时间:10/20/2023 最后编辑:HangarRashAniket Rao 更新时间:10/21/2023 访问量:40

问:

我目前正在从事一个 iOS 项目,其中我使用 Core Data 进行本地数据存储。现在,我想将我的.sqlite数据库文件上传到iCloud,以便进行同步和备份。有人可以指导我完成此任务的步骤和最佳实践吗?

  • 如何配置我的核心数据堆栈以与 iCloud 配合使用?
  • 以编程方式将我的 .sqlite 数据库文件上传到 iCloud 云盘的步骤是什么?
  • 在处理 iCloud 和 Core Data 集成时,是否有任何注意事项或潜在问题需要注意?

我在这里附上我的备份文件代码块。

class BackUp : NSObject {
    
    var query: NSMetadataQuery!
      
        let iCloudContainerIdentifier = "-----------"
        private let databaseName = "projectName.sqlite"
        let container = NSPersistentContainer(name: "projectName")
    
      override init() {
          super.init()
          initialiseQuery()
          addNotificationObservers()
       }
    
    
    func initialiseQuery() {
           
           query = NSMetadataQuery.init()
           query.operationQueue = .main
           query.searchScopes = [NSMetadataQueryUbiquitousDataScope]
           query.predicate = NSPredicate(format: "%K LIKE %@", NSMetadataItemFSNameKey, databaseName)
       }
    
    
        private func retrieveLocalURL() -> URL {
            let urls = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
            let documentaryDirectory = urls[0]
            let realmURL = documentaryDirectory.appendingPathComponent("Yoodoo.sqlite");
    
            return realmURL
        }
    
    func startBackup() throws {
            let fileURL =  retrieveLocalURL()//Bundle.main.url(forResource: "projectName", withExtension: "sqlite") else { return }
        
        if let containerURL = FileManager.default.url(forUbiquityContainerIdentifier: iCloudContainerIdentifier) {
            if !FileManager.default.fileExists(atPath: containerURL.path) {
                try FileManager.default.createDirectory(at: containerURL, withIntermediateDirectories: true, attributes: nil)
            }
            let backupFileURL = containerURL.appendingPathComponent(databaseName)
            if FileManager.default.fileExists(atPath: backupFileURL.path) {
                try FileManager.default.removeItem(at: backupFileURL)
                try FileManager.default.copyItem(at: fileURL, to: backupFileURL)
            } else {
                try FileManager.default.copyItem(at: fileURL, to: backupFileURL)
            }
            
            query.operationQueue?.addOperation({ [weak self] in
                _ = self?.query.start()
                self?.query.enableUpdates()
            })
        } else{
            print("Container Not Found...!")
        }
        
//        guard let containerURL = FileManager.default.url(forUbiquityContainerIdentifier: iCloudContainerIdentifier) else { return }
            
           
        }
    
    
    func addNotificationObservers() {
                            
            NotificationCenter.default.addObserver(forName: NSNotification.Name.NSMetadataQueryDidStartGathering, object: query, queue: query.operationQueue) { (notification) in
                self.processCloudFiles()
            }
            
            NotificationCenter.default.addObserver(forName: NSNotification.Name.NSMetadataQueryDidUpdate, object: query, queue: query.operationQueue) { (notification) in
                self.processCloudFiles()
            }
        }
    
    
    @objc func processCloudFiles() {
            
            if query.results.count == 0 { return }
            var fileItem: NSMetadataItem?
            var fileURL: URL?
            
            for item in query.results {
                
                guard let item = item as? NSMetadataItem else { continue }
                guard let fileItemURL = item.value(forAttribute: NSMetadataItemURLKey) as? URL else { continue }
                if fileItemURL.lastPathComponent.contains(databaseName) {
                    fileItem = item
                    fileURL = fileItemURL
                }
            }
            
            let fileValues = try? fileURL!.resourceValues(forKeys: [URLResourceKey.ubiquitousItemIsUploadingKey])
            if let fileUploaded = fileItem?.value(forAttribute: NSMetadataUbiquitousItemIsUploadedKey) as? Bool, fileUploaded == true, fileValues?.ubiquitousItemIsUploading == false {
                print("backup upload complete")

            } else if let error = fileValues?.ubiquitousItemUploadingError {
                print("upload error---", error.localizedDescription)
                
            } else {
                if let fileProgress = fileItem?.value(forAttribute: NSMetadataUbiquitousItemPercentUploadedKey) as? Double {
                    print("uploaded percent ---", fileProgress)
                }
            }
        }
    
    func restoreBackup() throws {
        guard let containerURL = FileManager.default.url(forUbiquityContainerIdentifier: iCloudContainerIdentifier) else {
            print("Container Not Found...!")
            return
        }

        let backupFileURL = containerURL.appendingPathComponent(databaseName)

        if FileManager.default.fileExists(atPath: backupFileURL.path) {
            let localURL = retrieveLocalURL()

            do {
                if FileManager.default.fileExists(atPath: localURL.path) {
                    try FileManager.default.removeItem(at: localURL)
                }
                try FileManager.default.copyItem(at: backupFileURL, to: localURL)
                print("Data restored successfully.")
            } catch {
                print("Failed to restore data: \(error.localizedDescription)")
            }
        } else {
            print("Backup file not found in iCloud.")
        }
    }
}
  • 我已经在 Xcode 中启用了 iCloud 的功能,并且还创建了手动和自动这两个 iCloud 容器来存储数据。
  • 此外,我还尝试了使用不同类型的数据库对象(如privateDatabase和Public Database)的不同函数。

期望:

  • 我希望整个 .sqlite 数据库文件(包含所有表)应该上传到 iCloud 上,每当我们再次安装应用程序并且 iCloud 上存在 .sqlite 数据库文件时,它应该替换为本地文件。
iOS Swift Core-数据 iCloud

评论


答: 暂无答案