在 iOS 8 中将应用程序的无处不在的容器暴露给 iCloud Drive

Posted

技术标签:

【中文标题】在 iOS 8 中将应用程序的无处不在的容器暴露给 iCloud Drive【英文标题】:Exposing an app's ubiquitous container to iCloud Drive in iOS 8 【发布时间】:2014-08-08 12:36:04 【问题描述】:

我正在开发一个支持 iCloud 的应用程序,用户将能够通过 iCloud Drive 导入和导出文件。在浏览 iCloud Drive 时,无论是使用 UIDocumentPickerViewController (ios 8) 还是 Finder (OS X Yosemite),我都可以看到由其他支持 iCloud-Drive 的应用程序创建/拥有的目录,例如 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>

这些键也记录在here,但我还没有找到任何其他关于更广泛主题的文档。 编辑/注意:虽然它不包含我的问题的答案,但Document Picker Programming Guide 是一个有用的资源。

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

对于第 3 方应用程序,iCloud 容器以这种方式构建:iCloud.$(CFBundleIdentifier)。我不确定为什么 TextEdit 只使用纯包标识符,但对于我们的标识符,我已经尝试了这两种方法,即有和没有 iCloud. 前缀。我还认识到您需要对捆绑标识符进行硬编码(即,不要使用 iCloud.$(CFBundleIdentifier)),因为在构建时似乎只解析了 PLIST 的值,而不是键。

李>

我以编程方式添加了一个子目录(到&lt;containerPath&gt;/Documents),因此容器不为空。不过,这并不重要,因为所有其他应用的目录最初也是空的。

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

iCloud 设置正确,我可以使用[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]; 返回的 URL 以编程方式查看 ubiquity 容器。

我登录到启用了 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/>

我已经在 Capabilities 部分使用 Xcode 的 UI 进行了设置。我不明白为什么最后一个键没有条目,但添加 &lt;string&gt;iCloud.$(CFBundleIdentifier)&lt;/string&gt; 并没有帮助。相反,它使 Xcode 在 Capabilities UI 中抱怨,所以我将其删除。 编辑/注意:在 Xcode beta 6 中,此问题已得到修复,即需要设置 ubiquity 容器标识符,Xcode 可以为您修复。

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

编辑:

我尝试过的另外两件事:

按照 Erikmitk 的建议,将(可选)NSUbiquitousContainerName 键(+ 值)添加到特定于容器的字典中。

仅将 NSUbiquitousContainerIsDocumentScopePublic 键/值添加到 PLIST 根字典而不是容器特定字典中,就像在 WWDC sample apps 之一中完成的那样(查找 NewBox)。

李>

【问题讨论】:

只是想确保:您没有使用“com.apple.TextEdit”本身作为 Info.plist 中的键,对吧?您应该使用您在权利文件中指定的 com.apple.developer.icloud-container-identifiers 的值。 是的,我正在使用我们自己的容器标识符。 【参考方案1】:

我的应用程序遇到了类似的问题。我能够通过执行以下操作来完成这项工作:

    根据此处https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/ExtensibilityPG/FileProvider.html 的文档,将NSUbiquitousContainers 设置添加到我的Info.plist 文件中。以下是相关代码:

    <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>
    

    重要!然后我将上面的NSUbiquitousContainerSupportedFolderLevels字符串值从Any更改为One

    <key>NSUbiquitousContainerSupportedFolderLevels</key>
    <string>One</string>
    

    接下来,也是最后,我必须将CFBundleVersion 更改为更高版本。我还将CFBundleShortVersionString 也升级到了新版本。

构建并运行之后,带有我的应用程序图标的文件夹正确出现在 iCloud Drive 中!希望这会有所帮助!

【讨论】:

为什么必须先将 SupportedFolderLevels 设置为“Any”,然后再将其设置为“One”? 似乎不需要将支持的文件夹级别设置为“一”。撞包版本号是关键 这就是为我解决的问题!太棒了,谢谢。【参考方案2】:

当您编辑 Info.plist 时,也许您忘记增加捆绑包版本号?这是WWDC session #234 的要求。

【讨论】:

我不知道,所以感谢您的提示和会话链接。但是,我们已经多次修改版本号(出于其他原因),所以这也没有减少。 再次,只是想确保:您是否将CFBundleVersion 的值更改为更高的数字? 我会在星期一再次检查,但我几乎 100% 确定我们做到了。 自更改以来,我们已经多次增加版本号,我只是尝试恢复/重新应用更改并增加捆绑版本号,但没有成功。 想不出其他可能导致问题的原因。 FWIW,这是对我有用的源代码:github.com/roop/GiveAndTake【参考方案3】:

关键是至少调用一次[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];(或者使用另一个容器标识符,如果它不是默认标识符)(不是每次启动,但可能是每个版本,或者在更改相应的 PLIST 条目之一时),以便初始化目录。我认为这一步需要结合增加捆绑版本号,如 roop 的回答中所建议的那样。

我注意到我的问题在这方面可能令人困惑,因为我提到能够使用相关 API 以编程方式查看文档目录*。但是,我稍后从应用程序中删除了该代码,也许是在正确设置其余部分之前。我不打算直接写入文档目录,只通过文档选择器。因此,无需获取 URL。

如果您只需要一个 Document Picker 来从 iCloud Drive 或其他应用程序的文档目录中读取/存储文件,则无需致电 URLForUbiquityContainerIdentifier:。仅当您希望您的应用拥有自己的通用容器(并可能在 iCloud Drive 和文档选择器中公开它)时,原始帖子中提到的步骤和对 URLForUbiquityContainerIdentifier: 的调用是必要的。

*当提到文档目录时,我总是指的是ubiquity容器中的那个,而不是本地那个。

【讨论】:

第一次运行应用前,Info.plist 中需要有NSUbiquitousContainerIsDocumentScopePublic=true 我用粗体标记的要点,必须在Info.plist中添加'NSUbiquitousContainerIsDocumentScopePublic=true' 在你第一次运行应用程序之前否则文件夹将被隐藏默认情况下,不是公用文件夹。如果在将此代码添加到 info.plist 之前已运行该应用程序,则卸载该应用程序,更改内部版本号并再次运行。 我明白了,感谢您的澄清。我必须承认我已经有一段时间没有研究这个问题了,但这听起来确实合理。谢谢你:)【参考方案4】:

看来,更改CFBundleVersion 可以让它工作。

我想你可以试试。我是从 Apple Developer Forums 那里得到的。

希望这对你有用。

【讨论】:

我还可以通过更改 NSUbiquitousContainerIsDocumentScopePublic 然后碰撞 CFBundleVersion 来告诉 macos 发生了更改来打开和关闭 iCloud 容器的可见性。这可靠地触发了文件夹可见性的变化。 是的@patrickkidd,关闭 NSUbiquitousContainerIsDocumentScopePublic 并使用版本凹凸然后再次打开版本凹凸确实有效。最简单的解决方案。【参考方案5】:

在整个上午都在闲逛,阅读所有帖子,进行所有更改之后,最终对我有用的关键是,正如 Yet Another Code Maker 所说,更改捆绑 ID。我认为一旦它为捆绑包创建了一个容器,您就无法返回并更改它的可见性以使其出现在 Finder 中。我已经尝试了所有不同的 info.plist 值,但在我更改为新的包名称并强制系统创建一个新包之前没有任何效果。顺便说一句,我没有在任何地方看到这一点,但包名称、NSUbiquitousContainer 名称和 NSUbiquitousContainerName 都可以不同——这就是我在我的案例中所做的。在花了这么多时间之后,我想我会继续在 GitHub 上放一个简单的示例应用程序,以防任何人在调试 Finder 中出现的 iCloud 文件夹时仍然遇到问题 - 你可以找到它 here。 README 中列出了所有必需的步骤。

【讨论】:

【参考方案6】:

在我的情况下(Xcode 7 和 iOS 9),经过多次尝试后,唯一使它起作用的就是使用新的包标识符(您不必更改云容器标识符,只需确保在 Apple Developer Member Center 中选择您要使用的容器,并在 Xcode 中指定自定义容器而不是默认容器。

事实上,这意味着您第一次运行应用程序时,必须设置 info.plist 的 NSUbiquitousContainers 部分。如果你之后将其设置为第二步,它将无法正常工作...

【讨论】:

绝对正确!经过这么多天的斗争,我的问题得到了解决。谢谢!! IIRC 您必须升级版本才能重新扫描 plist 中的更改。【参考方案7】:

documentation page 上的 .plist 条目有一个附加条目:

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

也许缺少的名字阻止它出现。

【讨论】:

不幸的是,这没有帮助。根据我链接的文档,它是可选的:"NSUbiquitousContainerName (String - iOS and OS X) Specifies the name that the iCloud Drive displays for your container. By default, the iCloud Drive will use the name of the bundle that owns the container." 我仍然添加了它,但没有任何效果。 是的,我读到了。但我认为无论如何都值得一试。 devforums.apple.com 上也有同样的问题,但还没有答案。所以我会说这是一个错误或尚不可用。对我来说,iCloudDrive 在优胜美地仍然很不稳定……:/【参考方案8】:

找不到任何文档,但经过反复试验,我发现:

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

为您提供驱动器的基本 URL,如选择器中所示。使用这个基本 URL,我可以将文件保存在我的应用程序中,并在优胜美地的 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

【讨论】:

我没有尝试将常规文件保存在 iCloud Drive 中 - 这也适用于 Document Picker。我正在尝试公开与我的应用关联的现有 Documents 目录。该目录存在(我可以通过编程方式或在优胜美地的终端中验证),但它隐藏在 Apple 的标准 UI(查找器和文档选择器)中。 您的 Info.plist 添加对我有用。详情见上。 感谢您试用。我认为由于某种原因我遇到了错误,但一般方法似乎是正确的。【参考方案9】:

只是想强调 OP 的发现之一,它为我修复了它:

我还认识到您需要对捆绑标识符进行硬编码(即,不要使用 iCloud.$(CFBundleIdentifier)),因为在构建时似乎只解析了 PLIST 的值,而不是键。

您需要对捆绑包 ID 进行硬编码。同时更新版本。

(直到我看完所有答案后才注意到问题中的这一点)。

【讨论】:

【参考方案10】:

好吧,它没有记录在任何地方,但尝试在容器中添加Documents 文件夹并将您的文件存储在那里。

在this Apple Developer Forum thread 的回复中发现了这个提示。

【讨论】:

【参考方案11】:

我的 OSX 应用也出现了同样的问题。

似乎 NSUbiquitousContainers 设置仅适用于 iCloud 容器的创建时间。所以我尝试使用新的 Apple ID(用于准备干净的 iCloud 环境),它就可以工作了。

【讨论】:

【参考方案12】:

我知道这是一个旧线程,但以防万一有人遇到同样的问题:让我的 Container 文件夹在 iCloud Drive 中可见的唯一方法(在尝试了上述所有建议之后)是让我的应用程序创建Documents 文件夹中的临时文件。一旦我这样做了,容器文件夹(以及我创建的文件)就会出现在我的 Mac 上。如果确实是这种情况,我必须创建一个文件以使该文件夹可见,那么这会有点烦人,因为我的应用程序是只读应用程序(仅读取用户添加到容器文件夹的文件)。首次启动应用程序时,容器文件夹需要可见。我想我将不得不检测第一次发射。

【讨论】:

以上是关于在 iOS 8 中将应用程序的无处不在的容器暴露给 iCloud Drive的主要内容,如果未能解决你的问题,请参考以下文章

在 Mono 中将 C++ 暴露给 C#:函数的无效转换?

我错过了啥? Flexbox 不在此代码段中将容器居中 [重复]

如何在 iOS 应用程序中将传输流转换(重新包装)为 MPEG-4 容器?

似乎无法将 docker 容器端口暴露给主机

如何在没有端口映射的情况下将 docker 容器的 ip 和端口暴露给外部 docker 主机?

是否可以将 USB 设备暴露给 LXC/Docker 容器?