【问题标题】:Best way to use iCloud Documents Storage使用 iCloud 文档存储的最佳方式
【发布时间】:2016-02-26 11:48:42
【问题描述】:

我目前在我的 iOS 应用程序中使用本地存储。用户数据存储在 Document Directory 中,现在我打算使用 iCloud Documents 存储。

我打算这样做:

  1. 检查 iCloud 在设备上是否可用

  2. 如果是,使用 URLForUbiquityContainerIdentifier 获取 iCloud 容器 URL

  3. 将新文件和文档保存到这个新 URL

为此,我使用此代码将返回文档文件夹的 URL(iCloud 或本地)

class CloudDataManager {

    class func getDocumentDiretoryURL() -> NSURL {
        let localDocumentsURL = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: .UserDomainMask).last! as NSURL
        let iCloudDocumentsURL = NSFileManager.defaultManager().URLForUbiquityContainerIdentifier(nil)?.URLByAppendingPathComponent("Documents")

        if userDefault.boolForKey("useCloud") && iCloudDocumentsURL != nil  {
            return iCloudDocumentsURL!
        } else {
            return localDocumentsURL
        }
    }
}

这是最佳实践吗? 我担心如果有一天 iCloud 不可用会出现问题,因此将使用本地目录而不是云容器并且为空。 谢谢。

【问题讨论】:

  • 我使用与您相同的代码删除 iCloud 文件,但该文件没有被删除。我只是想确认您的删除功能确实适用于您应用中的 iCloud 文件?
  • 我在我的应用程序中删除 iCloud 文件没有问题。有时可能需要超过 2 分钟才能真正看到文件从 iCloud 容器中消失。
  • 谢谢。我发现删除对我有用。但是,当我尝试更新 iCloud 中已经存在的文件时,我最终只会得到一个同名的新文件,其后有一个数字。因此,我决定首先检查文件是否在 iCloud 中,如果是,则删除然后添加具有相同名称的新文件。这仍然会导致保存副本,并且原始文件在那里(未删除)。但是,当我只是尝试删除一个文件时,它被删除了。奇怪……不知道为什么会这样。
  • 圣诞快乐。这取决于您如何编写数据。就我而言,我使用 NSKeyedArchiver.archiveRootObject 因为我的对象继承自 NSCoding。 Here is an explanation。这使您可以将自定义对象保存到存储文件中的数组中。要更新作为对象数组的数据,我从 iCloud 容器(或本地目录,如果未启用 iCloud)加载此文件的 URL,然后我将新数据附加到此数组,最后我使用 archiveRootObject 将新数组保存到文件系统自动写入文件
  • 否则如果你有很多文件要写入或更新,你可以使用 UIDocument 类来管理写入,如果你尝试从不同的设备同时写入,它也会自动避免冲突。如果您需要,请在 PM 中与我联系,如果您投票支持我的答案,我将不胜感激,谢谢 ;)

标签: ios swift icloud


【解决方案1】:

查看此链接:iCloud basics and code sample

如果您存储的信息很简单,最好使用 NSUserDefaults。您不想向 iCloud 询问基本信息。

【讨论】:

  • 谢谢,我使用 UserDefaults 来检查用户是否想使用 iCloud。我正在通过检查您提供给我的链接进行更多调查。
【解决方案2】:

感谢上面的评论和进一步阅读,我找到了解决问题的方法。

我决定这样做:

  • iCloud 将默认激活(如果可能)
  • 用户可以使用 UISwitch 在 App 中禁用/启用 iCloud
  • 当用户禁用 iCloud 时,所有 iCloud 文件都将传输到本地
  • 当用户启用 iCloud 时,所有本地文件都将传输到 iCloud Ubiquity 容器中
  • 没有数据合并

这样的数据不会丢失。

我猜几乎每个人都会使用 iCloud,一切都会变得透明且轻松。无论如何,我同步的文件都很小,所以应该可以正常工作(到目前为止)。

我有 5 个简单的方法:

  1. 检查 iCloud 是否可用的方法
  2. 根据用户选择(iCloud OR Local)返回文档 URL 的方法
  3. 删除目录中所有文件的方法(应用程序使用的文件)
  4. 将文件从本地目录移动到 iCloud 容器的方法
  5. 将文件从 iCloud 容器移动到本地目录的方法

这是我处理问题的班级

class CloudDataManager {

static let sharedInstance = CloudDataManager() // Singleton

struct DocumentsDirectory {
    static let localDocumentsURL: NSURL? = NSFileManager.defaultManager().URLsForDirectory(NSSearchPathDirectory.DocumentDirectory, inDomains: .UserDomainMask).last! as NSURL
   static let iCloudDocumentsURL: NSURL? = NSFileManager.defaultManager().URLForUbiquityContainerIdentifier(nil)?.URLByAppendingPathComponent("Documents")

}


// Return the Document directory (Cloud OR Local)
// To do in a background thread

func getDocumentDiretoryURL() -> NSURL {
    print(DocumentsDirectory.iCloudDocumentsURL)
    print(DocumentsDirectory.localDocumentsURL)
    if userDefault.boolForKey("useCloud") && isCloudEnabled()  {
        return DocumentsDirectory.iCloudDocumentsURL!
    } else {
        return DocumentsDirectory.localDocumentsURL!
    }
}

// Return true if iCloud is enabled

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

// Delete All files at URL

func deleteFilesInDirectory(url: NSURL?) {
    let fileManager = NSFileManager.defaultManager()
    let enumerator = fileManager.enumeratorAtPath(url!.path!)
    while let file = enumerator?.nextObject() as? String {

        do {
            try fileManager.removeItemAtURL(url!.URLByAppendingPathComponent(file))
            print("Files deleted")
        } catch let error as NSError {
            print("Failed deleting files : \(error)")
        }
    }
}

// Move local files to iCloud
// iCloud will be cleared before any operation
// No data merging

func moveFileToCloud() {
    if isCloudEnabled() {
        deleteFilesInDirectory(DocumentsDirectory.iCloudDocumentsURL!) // Clear destination
        let fileManager = NSFileManager.defaultManager()
        let enumerator = fileManager.enumeratorAtPath(DocumentsDirectory.localDocumentsURL!.path!)
        while let file = enumerator?.nextObject() as? String {

            do {
                try fileManager.setUbiquitous(true,
                    itemAtURL: DocumentsDirectory.localDocumentsURL!.URLByAppendingPathComponent(file),
                    destinationURL: DocumentsDirectory.iCloudDocumentsURL!.URLByAppendingPathComponent(file))
                print("Moved to iCloud")
            } catch let error as NSError {
                print("Failed to move file to Cloud : \(error)")
            }
        }
    }
}

// Move iCloud files to local directory
// Local dir will be cleared
// No data merging

func moveFileToLocal() {
    if isCloudEnabled() {
        deleteFilesInDirectory(DocumentsDirectory.localDocumentsURL!)
        let fileManager = NSFileManager.defaultManager()
        let enumerator = fileManager.enumeratorAtPath(DocumentsDirectory.iCloudDocumentsURL!.path!)
        while let file = enumerator?.nextObject() as? String {

            do {
                try fileManager.setUbiquitous(false,
                    itemAtURL: DocumentsDirectory.iCloudDocumentsURL!.URLByAppendingPathComponent(file),
                    destinationURL: DocumentsDirectory.localDocumentsURL!.URLByAppendingPathComponent(file))
                print("Moved to local dir")
            } catch let error as NSError {
                print("Failed to move file to local dir : \(error)")
            }
        }
    }
}



}

【讨论】:

  • 如果用户碰巧关闭了您对 iCloud 的访问,无论是通过禁用 iCloud 还是为您的特定应用程序禁用它,应用程序如何知道将其文件移动到本地(特别是如果它不是当时在后台打开),还是他们滞留在 iCloud 中?
  • 文件未在 iCloud 中显示任何特定问题。?
  • 我无法将 iColud 图像复制到我的本地存储中。您能帮我解决这个问题吗?
  • 你能分享更多的快照代码来展示如何实现你上面的代码来将文件复制到本地到 iCloud 驱动器吗?
  • 关于deleteFilesInDirectory方法:不应该先用fileManager.setUbiquitous(false.....把它们从云端删除,然后再本地删除吗?查看您的代码,iCloud 似乎不会收到更改通知,其他设备也不会收到文件已删除的更新。
【解决方案3】:

对于那些想要使用 SWIFT 3 的人: 注意:我只是复制而不是移动数据。但是目标路径在复制数据之前被清除..

class CloudDataManager {

    static let sharedInstance = CloudDataManager() // Singleton

    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")
    }


    // Return the Document directory (Cloud OR Local)
    // To do in a background thread

    func getDocumentDiretoryURL() -> URL {
        if isCloudEnabled()  {
            return DocumentsDirectory.iCloudDocumentsURL!
        } else {
            return DocumentsDirectory.localDocumentsURL
        }
    }

    // Return true if iCloud is enabled

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

    // Delete All files at URL

    func deleteFilesInDirectory(url: URL?) {
        let fileManager = FileManager.default
        let enumerator = fileManager.enumerator(atPath: url!.path)
        while let file = enumerator?.nextObject() as? String {

            do {
                try fileManager.removeItem(at: url!.appendingPathComponent(file))
                print("Files deleted")
            } catch let error as NSError {
                print("Failed deleting files : \(error)")
            }
        }
    }

    // Copy local files to iCloud
    // iCloud will be cleared before any operation
    // No data merging

    func copyFileToCloud() {
        if isCloudEnabled() {
            deleteFilesInDirectory(url: DocumentsDirectory.iCloudDocumentsURL!) // Clear all files in iCloud Doc Dir
            let fileManager = FileManager.default
            let enumerator = fileManager.enumerator(atPath: DocumentsDirectory.localDocumentsURL.path)
            while let file = enumerator?.nextObject() as? String {

                do {
                    try fileManager.copyItem(at: DocumentsDirectory.localDocumentsURL.appendingPathComponent(file), to: DocumentsDirectory.iCloudDocumentsURL!.appendingPathComponent(file))

                    print("Copied to iCloud")
                } catch let error as NSError {
                    print("Failed to move file to Cloud : \(error)")
                }
            }
        }
    }

    // Copy iCloud files to local directory
    // Local dir will be cleared
    // No data merging

    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")
                } catch let error as NSError {
                    print("Failed to move file to local dir : \(error)")
                }
            }
        }
    }



}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多