简体   繁体   English

备份 Realm 到 iCloud Drive

[英]Backup Realm to iCloud Drive

I would like to backup a realm database file to an iCloud drive, like WhatsApp, I have some questions:我想将领域数据库文件备份到 iCloud 驱动器,例如 WhatsApp,我有一些问题:

  1. What is the best practice to do this?这样做的最佳做法是什么?

  2. I have a database located in a shared group folder to access it from extensions, how can I back it up?我有一个位于共享组文件夹中的数据库,可以从扩展程序访问它,我该如何备份它? How can I show the progress bar of upload?如何显示上传进度条? Like WhatsApp for example?例如 WhatsApp?

  3. If I put a realm file in a document folder it will be synced for each modify.如果我将领域文件放在文档文件夹中,它将在每次修改时同步。

  4. Are there some samples code that we can see?是否有一些我们可以看到的示例代码?

Thanks for the help, have any ideas?感谢您的帮助,有什么想法吗? links?链接?

Just to clarify, this is a question about backing up a discrete Realm file itself to iCloud Drive, so that it would be visible in the iCloud Drive app.澄清一下,这是一个关于将独立的 Realm 文件本身备份到 iCloud Drive 的问题,以便它在 iCloud Drive 应用程序中可见。 Not synchronizing the contents of the file to a CloudKit store.不将文件内容同步到 CloudKit 存储。

If you leave the Realm file in the Documents directory, then if the user performs an iCloud or iTunes backup, the file will be backed up.如果您将 Realm 文件保留在 Documents 目录中,那么如果用户执行 iCloud 或 iTunes 备份,该文件将被备份。 All this means though is that if the user decides to upgrade to a new device and perform a restore using the old device's backup image, the Realm file will be restored then.所有这一切都意味着,如果用户决定升级到新设备并使用旧设备的备份映像执行恢复,那么 Realm 文件将被恢复。 If the user deletes the app from your old device before then, the iCloud backup will also be deleted.如果用户在此之前从您的旧设备中删除该应用程序,则 iCloud 备份也将被删除。

If you want to export your Realm file so it can be permanently saved and accessed in iCloud Drive, you can export a copy of the Realm file to your app's iCloud ubiquity container .如果您想导出您的 Realm 文件以便它可以在 iCloud Drive 中永久保存和访问,您可以将 Realm 文件的副本导出到您的应用程序的iCloud 无处不在容器 This is basically just another folder like the shared group's folder, but it's managed by iCloud.这基本上只是另一个文件夹,如共享组的文件夹,但它由 iCloud 管理。 This folder sort of behaves like Dropbox in that anything you put in there is automatically synchronized.此文件夹的行为类似于 Dropbox,因为您放入其中的任何内容都会自动同步。

The code would look something like this:代码看起来像这样:

let containerURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)
let realmArchiveURL = containerURL.appendPathComponent("MyArchivedRealm.realm")

let realm = try! Realm()
try! realm.writeCopy(toFile: realmArchiveURL)

This is a really basic example.这是一个非常基本的例子。 The Apple documentation recommends you do this on a background thread since setting up the iCloud folder for the first time can create some time. Apple 文档建议您在后台线程上执行此操作,因为第一次设置 iCloud 文件夹可能会创建一些时间。

Updating this wouldn't happen automatically.更新不会自动发生。 You'll need to export a new copy of the Realm each time the user wants to perform a backup.每次用户想要执行备份时,您都需要导出 Realm 的新副本。

I have recently had the same requirements and I am able to achieve from below steps我最近有相同的要求,我能够从以下步骤实现

Swift: 4+斯威夫特:4+

Step:1第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 

Step 2第2步

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

Step 3 - Check for cloud Enabled options for your iphone第 3 步 - 检查您 iphone 的启用云选项

// Return true if iCloud is enabled

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

Step 4 - Setup the below variables for Local or iCloud Document directories第 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")
}

Step:5 Below function is used for copyRealmFileToIcloudContainer步骤: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!)
    }
}

Step:6步骤: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

Step:7 - Make Sure you have added the below lines in info.plist步骤: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>

@yonlau as per your request sharing answer for backup realm file , This is tested once and the realm data only have when they backup on iCloud. @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.Copy realm file from iCloud to Document directory 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)")
            }
        }
    }
}

You could take a look at this Github project by mikemac8888 .你可以看看mikemac8888这个 Github 项目

Basically you make your model objects conform to RealmCloudObject :基本上你让你的模型对象符合RealmCloudObject

class Note: Object, RealmCloudObject {
  ...
}

You have to implement a mapping function :你必须实现一个映射函数:

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

... and the reverse function used to create Realm records out of CloudKit records: ...以及用于从 CloudKit 记录创建 Realm 记录的反向函数:

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

  ...
}

The full documentation could be read at the link I provided, obviously.显然,可以在我提供的链接中阅读完整的文档。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM