将文件保存到iCloud Drive

12

我正在尝试将文件保存到 iCloud Drive。我使用的是简单版本,不让用户选择保存文件的位置,而是直接将其保存在 iCloud Drive 的根目录中。这是我正在使用的代码:

func exportToFiles() {
    for page in pages {
            let filename = getDocumentsDirectory().appendingPathComponent((page.title ?? "Untitled") + ".png")
        if let image = exportImage(page: page, dpi: 300) {
            if let data = image.pngData() {
                try? data.write(to: filename)
            }
        }
    }
}

func getDocumentsDirectory() -> URL {
    let driveURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
    return driveURL!
}

我在我的 Info.plist 文件中有这个:

<key>NSUbiquitousContainers</key>
<dict>
        <key>iCloud.com.mysite.myapp</key>
        <dict>
                <key>NSUbiquitousContainerIsDocumentScopePublic</key>
                <true/>
                <key>NSUbiquitousContainerName</key>
                <string>myapp</string>
                <key>NSUbiquitousContainerSupportedFolderLevels</key>
                <string>Any</string>
        </dict>
</dict>

在用户界面上看起来好像它可以工作,因为我点击按钮后会有一个短暂的停顿。但是当我查看我的文件时,里面什么也没有。我做错了什么?


你是否仔细检查了info.plist文件中的键是否与“iCloud.bundleID”匹配,并在更改info.plist时递增构建号? - Paulw11
哦,我不知道。我修复了所有这些问题,但它仍然无法正常工作,所以还有其他问题:P 但是谢谢! - Vlad the Impala
3个回答

10

您需要检查并创建“文档”文件夹,并且处理错误是一个好的实践:

if let containerUrl = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents") {
    if !FileManager.default.fileExists(atPath: containerUrl.path, isDirectory: nil) {
        do {
            try FileManager.default.createDirectory(at: containerUrl, withIntermediateDirectories: true, attributes: nil)
        }
        catch {
            print(error.localizedDescription)
        }
    }
    
    let fileUrl = containerUrl.appendingPathComponent("hello.txt")
    do {
        try "Hello iCloud!".write(to: fileUrl, atomically: true, encoding: .utf8)
    }
    catch {
        print(error.localizedDescription)
    }
}

1
尝试了这个解决方案,但第一个if语句返回false...有什么想法吗? - Aviv Profesorsky
@AvivProfesorsky 我也有同样的问题,你找到解决方法了吗? - mica
1
@AvivProfesorsky,@mica;除了确保您的项目的iCloud/iCloud文档和iCloud/CloudKit功能已打开外,还要确保在iCloud能力设置中勾选一个特定的容器,通常命名为:iCloud.com.<您的公司名称>.<您的应用程序名称>。 - Yohst

7

使用SwiftUI、Swift 5、Xcode 13.2.1和iOS 15.2更新此内容至2022年中:

  • 在iOS应用程序目标的“签名和功能”选项卡下,单击“+功能”按钮。在出现的对话框中,向左滚动以找到“iCloud”,并双击它。
  • 回到“签名和功能”,会出现一个新的部分。勾选“iCloud文档”,并在下面查找“容器”列表。在第一条条目中,复制并粘贴“Bundle Identifier”的值(位于同一窗格顶部附近)。编辑容器条目,使其格式为iCloud.domain.orgName.appName。例如,如果您的bundle identifier是foo.bar.bazApp,则容器列表中的第一个条目应该是iCloud.foo.bar.bazApp。完成后,第一个条目应该是被勾选的。
  • 如果您没有.plist文件,则需要创建一个。它应该是导航器窗格中的第一个或第二个文件,并且它的名称应采用bazApp--iOS--Info的形式。单击它进行编辑。它应该看起来像这样 - 请注意,您的容器名称的最后一部分需要在NSUbiquitousContainerName下是一个精确的匹配。继续以iCloud.foo.bar.bazApp为例:
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>NSUbiquitousContainers</key>
  <dict>
      <key>iCloud.foo.bar.bazApp</key>
      <dict>
          <key>NSUbiquitousContainerIsDocumentScopePublic</key>
          <true/>
          <key>NSUbiquitousContainerName</key>
          <string>bazApp</string>
          <key>NSUbiquitousContainerSupportedFolderLevels</key>
          <string>Any</string>
      </dict>
  </dict>
</dict>
</plist>
  • 在您的实际应用程序代码中,使用上述列出的示例编写测试文件。
  • 在 iOS 目标的“常规”选项卡下,在“标识”部分中,如果已对 .plist 文件进行任何更改,则递增版本和构建字段。

如果您已正确设置所有内容,则在手机或模拟器的“文件”应用程序>iCloud Drive 中,您应该会看到一个带有应用程序名称和应用程序图标的新文件夹,就像任何其他特定于应用程序的 iCloud 文件夹一样。里面应该是您的新测试文件。如果您像我一样没有在 .plist 中准确输入 NSUbiquitousContainerName 值,那么您的代码将运行,但测试文件将只被写入“设置”> [您的姓名] > iCloud > 管理存储 > [您的应用程序名称] > 文档。这里的一些其他答案表明 NSUbiquitousContainerName 可以是您喜欢的任何名称,但我的经验是它必须与容器名称的结尾部分完全匹配。


1
我按照您概述的每个步骤操作,但该文件夹未在“文件”应用程序中显示。我创建了一个最小可重现示例来演示这个问题,您知道原因吗?这些步骤已更改吗? - erotsppa

1
请确保在"签名和能力"选项卡中,您已经勾选了iCloud选项卡中的"iCloud文档"选项。同时创建一个与您在plist文件中指定的完全相同的容器名称 - "myapp"。
<key>NSUbiquitousContainerName</key>
<string>myapp</string>

exportImage(page: page, dpi: 300)的返回值是什么?请确保它返回UIImage对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接