【问题标题】:Backup Realm to iCloud Drive将 Realm 备份到 iCloud Drive
【发布时间】:2017-01-27 14:51:50
【问题描述】:

我想将领域数据库文件备份到 iCloud 驱动器,例如 WhatsApp,我有一些问题:

  1. 执行此操作的最佳做​​法是什么?

  2. 我有一个位于共享组文件夹中的数据库,可以从扩展程序访问它,我该如何备份它?如何显示上传进度条?比如像 WhatsApp?

  3. 如果我将领域文件放在文件夹中,每次修改都会同步该文件。

  4. 是否有一些我们可以看到的示例代码?

感谢您的帮助,有什么想法吗?链接?

【问题讨论】:

    标签: ios objective-c swift realm


    【解决方案1】:

    澄清一下,这是一个关于将离散的 Realm 文件本身备份到 iCloud Drive 的问题,以便它在 iCloud Drive 应用程序中可见。未将文件内容同步到 CloudKit 存储。

    如果您将 Realm 文件保留在 Documents 目录中,那么如果用户执行 iCloud 或 iTunes 备份,该文件将被备份。这意味着如果用户决定升级到新设备并使用旧设备的备份映像执行恢复,那么 Realm 文件将被恢复。如果用户在此之前从您的旧设备中删除了该应用程序,iCloud 备份也将被删除。

    如果您想导出您的 Realm 文件,以便在 iCloud Drive 中永久保存和访问,您可以将 Realm 文件的副本导出到您应用的iCloud ubiquity container。这基本上只是另一个文件夹,就像共享组的文件夹一样,但它由 iCloud 管理。这个文件夹有点像 Dropbox,因为你放在那里的任何东西都会自动同步。

    代码如下所示:

    let containerURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)
    let realmArchiveURL = containerURL.appendPathComponent("MyArchivedRealm.realm")
    
    let realm = try! Realm()
    try! realm.writeCopy(toFile: realmArchiveURL)
    

    这是一个非常基本的示例。 Apple 文档建议您在后台线程上执行此操作,因为第一次设置 iCloud 文件夹可能需要一些时间。

    更新不会自动发生。每次用户想要执行备份时,您都需要导出 Realm 的新副本。

    【讨论】:

    • 好吧,完美,我将副本复制到 ubiquity 容器,但是如果用户删除我的应用程序,然后重新安装...我可以显示保存在他的 iCloud Drive 中的备份列表并决定恢复吗?或者 iCloud 会自动重新同步这个文件夹而不向用户询问任何内容?
    • 我个人没有亲自尝试过,所以我不能肯定,但是是的,我希望数据能够保留在 ubiquity 容器中,即使应用程序在原版上被删除设备。
    【解决方案2】:

    我最近有同样的要求,我可以通过以下步骤实现

    斯威夫特:4+

    步骤:1

     1.Setup Your cloudKit for your app with a Developer account
     2. You can take reference: https://www.raywenderlich.com/1000-cloudkit-tutorial-getting-started 
    

    第 2 步

     - Add CloudKit Capabilities in your App
     - Please check out the screenshot: https://prnt.sc/pdpda5
    

    第 3 步 - 检查您的 iPhone 的云启用选项

    // Return true if iCloud is enabled
    
    func isCloudEnabled() -> Bool {
        if DocumentsDirectory.iCloudDocumentsURL != nil { return true }
        else { return false }
    }
    

    第 4 步 - 为本地或 iCloud 文档目录设置以下变量

    struct DocumentsDirectory {
        static let localDocumentsURL = FileManager.default.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: .userDomainMask).last!
        static let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
    }
    

    步骤:5 下面的函数用于copyRealmFileToIcloudContainer

    func uploadDatabaseToCloudDrive()
    {
        if(isCloudEnabled() == false)
        {
            self.iCloudSetupNotAvailable()
            return
        }
    
        let fileManager = FileManager.default
    
        self.checkForExistingDir()
    
        let iCloudDocumentsURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents", isDirectory: true)
    
        let iCloudDocumentToCheckURL = iCloudDocumentsURL?.appendingPathComponent("\(memberId)_default.realm", isDirectory: false)
    
        let realmArchiveURL = iCloudDocumentToCheckURL//containerURL?.appendingPathComponent("MyArchivedRealm.realm")
    
        if(fileManager.fileExists(atPath: realmArchiveURL?.path ?? ""))
        {
            do
            {
                try fileManager.removeItem(at: realmArchiveURL!)
                print("REPLACE")
                let realm = try! Realm()
                try! realm.writeCopy(toFile: realmArchiveURL!)
    
            }catch
            {
                print("ERR")
            }
        }
        else
        {
            print("Need to store ")
            let realm = try! Realm()
            try! realm.writeCopy(toFile: realmArchiveURL!)
        }
    }
    

    步骤:6

     - Once your realm file uploaded on the server , you can check this in your iPhone
     - Steps
         - 1.Go To Setting
         - 2.Go To iCloud
         - 3.Go To ManageStorage
         - 4.You will see your application there
         - 5.Tap on Application, you will able to see your realm file over there
    

    步骤:7 - 确保您在 info.plist 中添加了以下行

    <key>NSUbiquitousContainers</key>
    <dict>
        <key>iCloud.com.example.app</key>
        <dict>
            <key>NSUbiquitousContainerIsDocumentScopePublic</key>
            <true/>
            <key>NSUbiquitousContainerName</key>
            <string>iCloudDemoApp</string>
            <key>NSUbiquitousContainerSupportedFolderLevels</key>
            <string>Any</string>
        </dict>
    </dict>
    

    【讨论】:

    • 嗨 Anita,你还知道如何在备份后将领域数据恢复到应用程序中吗?
    • 这里能提供相关代码吗?感谢您提供帮助。
    • 我已经为你添加了一个代码,请检查一下,如果有帮助,请点赞 :) 快乐 coading。
    • 欢迎,希望对你有帮助!!
    • checkForExistingDir() 在哪里/是什么?还有memberId?
    【解决方案3】:

    @yonlau 根据您的请求共享备份领域文件的答案,这是测试一次,领域数据仅在它们在 iCloud 上备份时才有。

    func DownloadDatabaseFromICloud()
    {
        let fileManager = FileManager.default
        // Browse your icloud container to find the file you want
        if let icloudFolderURL = DocumentsDirectory.iCloudDocumentsURL,
            let urls = try? fileManager.contentsOfDirectory(at: icloudFolderURL, includingPropertiesForKeys: nil, options: []) {
    
            // Here select the file url you are interested in (for the exemple we take the first)
            if let myURL = urls.first {
                // We have our url
                var lastPathComponent = myURL.lastPathComponent
                if lastPathComponent.contains(".icloud") {
                    // Delete the "." which is at the beginning of the file name
                    lastPathComponent.removeFirst()
                    let folderPath = myURL.deletingLastPathComponent().path
                    let downloadedFilePath = folderPath + "/" + lastPathComponent.replacingOccurrences(of: ".icloud", with: "")
                    var isDownloaded = false
                    while !isDownloaded {
                        if fileManager.fileExists(atPath: downloadedFilePath) {
                            isDownloaded = true
                            print("REALM FILE SUCCESSFULLY DOWNLOADED")
                            self.copyFileToLocal()
    
                        }
                        else
                        {
                            // This simple code launch the download
                            do {
                                try fileManager.startDownloadingUbiquitousItem(at: myURL )
                            } catch {
                                print("Unexpected error: \(error).")
                            }
                        }
                    }
    
    
                    // Do what you want with your downloaded file at path contains in variable "downloadedFilePath"
                }
            }
        }
    }
    

    2.从iCloud复制realm文件到Document目录

    func copyFileToLocal() {
        if isCloudEnabled() {
            deleteFilesInDirectory(url: DocumentsDirectory.localDocumentsURL)
            let fileManager = FileManager.default
            let enumerator = fileManager.enumerator(atPath: DocumentsDirectory.iCloudDocumentsURL!.path)
            while let file = enumerator?.nextObject() as? String {
    
                do {
                    try fileManager.copyItem(at: DocumentsDirectory.iCloudDocumentsURL!.appendingPathComponent(file), to: DocumentsDirectory.localDocumentsURL.appendingPathComponent(file))
    
                    print("Moved to local dir")
    
                    //HERE ACCESSING DATA AVAILABLE IN REALM GET FROM ICLOUD
                    let realm =  RealmManager()
                    let array = realm.FetchObjects(type: Mood.self)
                    print(array?.count)
    
                } catch let error as NSError {
                    print("Failed to move file to local dir : \(error)")
                }
            }
        }
    }
    

    【讨论】:

    • 谢谢。从 iCloud 下载文件工作正常。但我遇到了一个问题。当我删除旧领域文件并将新文件从 iCloud 复制到默认领域文件路径时。数据未反映在 UI 中。要获取数据,我必须杀死应用程序并重新启动。然后它将完美运行。无论如何,您都可以提供帮助。
    • 您好 Anita,感谢您提供的代码,这对我将 iCloud 文件保存到本地目录有很大帮助,但主要问题与上述相同 - 我必须重新运行应用程序才能在 UI 部分获取更新的数据,因为默认情况下,领域文件不显示数据应用程序,它仅在我再次运行应用程序时才有效 - 如果可能,请帮助我。
    • 嗨@AkashThakkar,我很高兴它对您有所帮助,应用程序重新运行的概念是由于领域文件引用,尝试为您的领域文件提供新名称并设置新的引用或尝试检查它读取的领域文件以及如何使用新文件设置引用
    • @Anita,感谢您的快速回复-我尝试做同样的事情和其他一些事情,但它对我不起作用-如果您可以提供一些可能有助于我这样做的代码将是高度赞赏
    • 到目前为止,我已经完成了这段代码,并且在另一家公司工作,现在我正在做自己的代码,所以很遗憾我没有代码,否则我肯定会不问就给你 对不起
    【解决方案4】:

    您可以通过mikemac8888 看看this Github project

    基本上你让你的模型对象符合RealmCloudObject

    class Note: Object, RealmCloudObject {
      ...
    }
    

    你必须实现一个映射功能:

    func toRecord() -> CKRecord {
      ...
      record["text"] = self.text
      record["dateModified"] = self.dateModified
    }
    

    ...以及用于从 CloudKit 记录中创建 Realm 记录的反向函数:

    public func changeLocalRecord(...) throws {
      ...
      realm.create(objectClass as! Object.Type,
          value: ["id": id,
              "text": text,
              "dateModified": NSDate(),
              "ckSystemFields": recordToLocalData(record)],
          update: true)
    
      ...
    }
    

    显然,可以在我提供的链接上阅读完整的文档。

    【讨论】:

      猜你喜欢
      • 2022-07-15
      • 1970-01-01
      • 1970-01-01
      • 2013-04-17
      • 1970-01-01
      • 2019-06-06
      • 2012-10-12
      • 2017-10-17
      • 2013-02-03
      相关资源
      最近更新 更多