在iOS 8中将应用程序的普遍容器暴露给iCloud Drive

32

我正在开发一款支持iCloud的应用程序,用户将能够通过iCloud Drive导入和导出文件。当使用UIDocumentPickerViewController(iOS 8)或Finder(OS X Yosemite)浏览iCloud Drive时,我可以看到其他支持iCloud的应用程序创建/拥有的目录,例如Automator、Keynote或TextEdit。

我希望我们的应用程序也能在iCloud Drive中公开其普遍文档目录,但是我还没有想出如何实现。在一些前述应用程序的Info.plist文件中,我发现了这个关键字:

<key>NSUbiquitousContainers</key>
<dict>
    <key>com.apple.TextEdit</key>
    <dict>
        <key>NSUbiquitousContainerIsDocumentScopePublic</key>
        <true/>
        <key>NSUbiquitousContainerSupportedFolderLevels</key>
        <string>Any</string>
    </dict>
</dict>
这些键在此处也有记录,但我没有找到其他更广泛主题的文档。 编辑/注释:虽然它不包含我的问题答案,但Document Picker编程指南是一个有用的资源。

我尝试将上述键值添加到我们的应用程序中,但没有看到任何效果。 我注意到/尝试过的事情:

  • 对于第三方应用程序,iCloud容器是这样构建的: iCloud。$(CFBundleIdentifier)。 我不确定为什么TextEdit仅使用纯束缚标识符,但对于我们的标识符,我尝试了两种方法,即带有和不带有iCloud。前缀。 我还意识到您需要硬编码束缚标识符(即不要使用iCloud。$(CFBundleIdentifier)),因为只有PLIST的值似乎在构建时解析,而不是键。

  • 我已经通过编程方式添加了一个子目录(到<containerPath>/Documents),因此容器不为空。 但是,这不应该重要,因为所有其他应用程序的目录最初也是空的。

  • 出现在iCloud Drive中的某些Apple应用程序在其Info.plist中没有这些条目,例如Numbers和Pages。

  • iCloud已正确设置,并且我可以以编程方式查看通过[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] 返回的URL中的普及容器。

  • 我登录了启用了iCloud Drive的iCloud帐户。 我可以在UIDocumentPickerViewController中看到我的iCloud Drive内容。

  • 我使用iOS 8 beta 5模拟器(和Yosemite beta 5在Mac上查看iCloud Drive目录)(编辑/注释:这同样适用于beta 6)

这是我的权限文件的外观(仅相关部分)

<key>com.apple.developer.icloud-container-identifiers</key>
<array>
    <string>iCloud.$(CFBundleIdentifier)</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
    <string>CloudDocuments</string>
</array>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array/>

我已经在Xcode的“功能”部分使用UI进行设置。我不明白为什么最后一个键没有输入,但添加<string>iCloud.$(CFBundleIdentifier)</string>没有帮助。相反,这使得Xcode在功能UI中抱怨,所以我已经将其删除。编辑/注:在Xcode beta 6中,已经修复了这个问题,即需要设置普及容器标识符,Xcode可以为您解决。

原始问题: 那么...这是一个bug吗? 它还不能工作吗? 我做错了吗?我在发布说明中找不到已知问题。

编辑:

我尝试了两个更多的东西:

  • 向容器特定字典中添加(可选的)NSUbiquitousContainerName键(+值),如Erikmitk建议的那样。

  • 仅向PLIST根字典添加NSUbiquitousContainerIsDocumentScopePublic键/值,而不是容器特定字典中的键/值,就像在其中一个WWDC示例应用程序(寻找NewBox)中所做的那样。


只是想确保一下:您没有将“com.apple.TextEdit”本身用作Info.plist中的键,是吗?您应该使用在授权文件中指定的com.apple.developer.icloud-container-identifiers的值。 - roop
是的,我正在使用我们自己的容器标识符。 - hagi
12个回答

20

我也遇到了一个类似的应用问题。通过以下方法,我成功解决了这个问题:

  1. 根据文档在我的 Info.plist 文件中添加了 NSUbiquitousContainers 设置。下面是相关代码:

    <dict>
        <!-- ... other top-level Info.plist settings ... -->
        <key>NSUbiquitousContainers</key>
        <dict>
            <key>iCloud.com.example.MyApp</key>
            <dict>
                <key>NSUbiquitousContainerIsDocumentScopePublic</key>
                <true/>
                <key>NSUbiquitousContainerSupportedFolderLevels</key>
                <string>Any</string>
                <key>NSUbiquitousContainerName</key>
                <string>MyApp</string>
            </dict>
        </dict>
    </dict>
    
  2. 重要! 然后我将上面的NSUbiquitousContainerSupportedFolderLevels字符串值从Any更改为One

  3. <key>NSUbiquitousContainerSupportedFolderLevels</key>
    <string>One</string>
    
  4. 接下来,最后一步,我需要将CFBundleVersion更改为更高的版本号。我还将CFBundleShortVersionString更新为一个新版本。

完成以上步骤后,我的应用程序图标所在的文件夹在iCloud Drive中正常显示了!希望这对你有帮助!


为什么在将SupportedFolderLevels设置成"One"之前需要先将其设置成"Any"呢? - Marcus
1
似乎将支持的文件夹级别设置为One并不是必要的。提高捆绑版本号才是关键。 - Derek Knight
这就是解决我的方法!太棒了,谢谢。 - horseshoe7

16
当您编辑 Info.plist 时,可能忘记了提高捆绑版本号?根据 WWDC session #234 的要求,这是必须的。

我不知道这个,所以感谢您的提示和会话链接。然而,由于其他原因,我们已经多次提高了版本号,所以这也无济于事。 - hagi
2
再确认一下,你是否将 CFBundleVersion 的值改为了更高的数字? - roop
我星期一会再仔细检查,但我几乎可以肯定我们做了。 - hagi
自从更改以来,我们已经多次增加了版本号,我刚刚尝试了还原/重新应用更改并提高捆绑版本号,但没有成功。 - hagi
我想不出还有什么其他可能导致问题的原因了。话说,这是我的源代码,对我而言它是可行的:https://github.com/roop/GiveAndTake/ - roop
谢谢!这非常重要。 - Tim Arnold

8
重点是至少需要调用[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];一次(或者如果不是默认容器,则使用另一个容器标识符),以初始化目录。我认为这一步需要与增加捆绑版本号结合使用,正如roop的答案所建议的那样。
我注意到我的问题可能在这方面有些混淆,因为我提到可以使用相关API以编程方式查看文档目录。但是,我后来从应用程序中删除了该代码,可能是在正确设置其余部分之前。我不会直接写入文档目录,只能通过文档选择器进行操作。因此,没有必要获取URL。
如果您只需要一个文档选择器来从iCloud Drive或其他应用程序的文档目录中读取/存储文件,则无需调用URLForUbiquityContainerIdentifier:。仅当您希望您的应用程序拥有自己的普及容器(并潜在地将其公开在iCloud Drive和文档选择器中)时,才需要执行原始帖子中提到的步骤和调用URLForUbiquityContainerIdentifier:
*提到文档目录时,我总是指的是普及容器中的文档目录,而不是本地容器中的文档目录。

在第一次运行应用程序之前,您需要在Info.plist中设置NSUbiquitousContainerIsDocumentScopePublic=true - anas.p
1
我在粗体标记的主要点是,在第一次运行应用程序之前,必须在Info.plist中添加“NSUbiquitousContainerIsDocumentScopePublic=true”,否则该文件夹将默认隐藏,而不是公共文件夹。如果已经在info.plist中添加了此代码之前运行了应用程序,则需要卸载该应用程序,更改构建号并重新运行。 - anas.p
我明白了,感谢您的澄清。我必须承认我有一段时间没有研究过这个问题,但听起来很有道理。谢谢 :) - hagi

4

看起来,改变CFBundleVersion会让它工作。

我认为你可以尝试一下。我是从Apple开发者论坛得到的。

希望这对你有用。


我还可以通过更改 NSUbiquitousContainerIsDocumentScopePublic 并且增加 CFBundleVersion 来打开或关闭 iCloud 容器的可见性,以告诉 macOS 发生了更改。这可靠地触发了文件夹可见性的更改。 - patrickkidd
是的@patrickkidd,通过版本升级关闭NSUbiquitousContainerIsDocumentScopePublic,然后再通过版本升级打开它确实有效。这是最简单的解决方案。 - BillF

3
在整个早上的研究中,我尝试了所有的方法,读了所有的帖子,做了所有的更改。最终,当 Yet Another Code Maker 提出更改绑定标识符时,关键的一点终于起作用了。我认为一旦为捆绑包创建了一个容器,就不能返回并更改其可见性以使其在Finder中出现。我尝试了所有不同的info.plist值,但没有什么效果,直到我更改成一个新的捆绑名,并强制系统创建一个新的捆绑包。顺便说一下,在我的情况下,我没有看到任何地方注明捆绑名称、NSUbiquitousContainer名称和NSUbiquitousContainerName可以都是不同的。在花费这么多时间后,我想我会把一个简单的样例程序放在GitHub上,以防有人仍然有问题调试他们的iCloud文件夹出现在Finder中-你可以在这里找到它。所有所需步骤都在README中概述。

2
在我的情况下(Xcode 7和iOS 9),在多次尝试后,唯一使其工作的方法就是使用一个新的Bundle Identifier(您无需更改云容器标识符,只需确保在Apple开发人员会员中心中选择要使用的容器,并在Xcode中指定自定义容器而不是默认容器)。
实际上,这意味着第一次运行应用程序时,必须设置info.plist的NSUbiquitousContainers部分。如果您将其设置为第二步,它将无法正常工作...

非常正确!在经过多天的挣扎后,解决了我的问题。谢谢!! - BillF
如果我没记错的话,您需要升级版本才能重新扫描 plist 中的更改。 - pronebird

2

这对我解决了问题。我之前附加的是“MyAppName”,而不是“Documents”。更改为“Documents”后,它可以正常工作。 - de.

1

我想强调一下原帖作者发现的一个解决方法,对我很有帮助:

我也认识到你需要硬编码捆绑标识符(即,不要使用 iCloud.$(CFBundleIdentifier)) ,因为只有 PLIST 的值在构建时才会被解析,但不会解析键。

您需要硬编码捆绑标识符。同时更新版本。

(在阅读所有答案之前,我没有注意到这一点)。


1

在这个文档页面上,.plist 条目有一个额外的条目:

<key>NSUbiquitousContainerName</key>
<string>MyApp</string>

也许是由于缺少名称,导致它没有显示出来。

1
不幸的是,这并没有帮助。根据我链接的文档,它是可选的:“NSUbiquitousContainerName(String-iOS和OS X)指定iCloud Drive为您的容器显示的名称。默认情况下,iCloud Drive将使用拥有容器的包的名称。” 我已经添加了它,但没有任何效果。 - hagi
是的,我看过了。但我认为还是值得一试的。同样的问题也在devforums.apple.com上出现了,但还没有答案。所以我想这可能是一个bug或者还没有推出。对我来说,在Yosemite中iCloudDrive仍然有些不稳定... :/ - Erik

1

我没有找到任何文档,但通过反复试验,我发现:

[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:@"com.apple.CloudDocs"]; 

此函数为您提供在选择器中查看的驱动器的基本URL。使用此基本URL,我能够在我的应用程序中保存文件并在Yosemite中查看iCloud驱动器中的文件。

编辑14.8.14

我尝试了您的plist设置:

<key>NSUbiquitousContainers</key>
<dict>
    <key>iCloud.net.redacted.docTest</key>
    <dict>
        <key>NSUbiquitousContainerIsDocumentScopePublic</key>
        <true/>
        <key>NSUbiquitousContainerSupportedFolderLevels</key>
        <string>Any</string>
    </dict>
</dict>

在我的小型测试应用程序“docTest”中,它确实在Yosemite和文档选择器中暴露了空的“Documents”目录。
屏幕截图 http://spring-appstudio.com/picker-view.png

1
我并不是试图将常规文件保存在iCloud Drive中 - 使用文档选择器也可以正常工作。我正在尝试公开与我的应用程序相关联的现有文档目录。该目录存在(我可以通过编程或在Yosemite上的终端验证),但在苹果的标准UI(Finder和文档选择器)中它是隐藏的。 - hagi
1
你添加的 Info.plist 已经生效了,详情请见上文。 - Ryan C. Spring
谢谢您尝试了一下。我猜测出现了某些原因导致了错误,但总体方法似乎是正确的。 - hagi

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