具有用户选择的文件读写权限的 MacOS 应用程序被 Mac App Store 拒绝,因为 SQLite 在写入数据库时​​创建临时文件

Ste*_*rey 7 sqlite macos xcode mac-app-store swift

我有一个 MacOS 应用程序,我想将其放在 Mac App Store 上,因此必须将其放入沙盒中。使用该应用程序,用户创建多个 SQLite 数据库文件来存储他们创建的信息,有点类似于 Excel 电子表格文件,但我在应用程序中利用 SQLite 来创建、读取和编辑文件。他们将这些文件存储在 Mac 上的 Documents 文件夹中。我为此添加了一项权利:com.apple.security.files.user-selected.read-write。\n该应用程序可以很好地读取这些数据库文件,但如果我尝试写入它们,则会失败。

\n\n

根据 Mac 控制台应用程序的日志,错误为:

\n\n
\n

沙箱:MyApp Helpe(16378) 拒绝(1) 文件写入创建\n /Users/steve/Documents/myFile1.sqlite-journal

\n\n

违规:拒绝(1)文件写入创建/Users/steve/Documents/myFile1.sqlite-journal

\n
\n\n

所以它失败了,因为 SQLite 在幕后创建了一个与访问的文件同名的临时文件,但通过向其附加“-journal”来更改扩展名。因此,如果用户打开一个名为 myFile1.sqlite 的文件,它会打开并读取正常,但如果他们尝试写入该文件,SQLite 将在该过程中创建一个名为 myFile1.sqlite-journal 的临时文件,然后将其删除。但由于用户没有打开或保存名为 myFile1.sqlite-journal 的文件,因此该文件不在沙箱中,因此被拒绝。

\n\n

我通过在 Finder 中创建一个名为 myFile1.sqlite-journal 的空文件并从我的应用程序中打开它(从而将其作为用户选择的文件添加到沙箱中)来确认这是问题所在,然后能够写入 myFile1.sqlite。

\n\n

这是一个已知问题,每个文档似乎都有一个使用“相关项目”的解决方案:\n https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth。 html

\n\n

下面是相关文本,但这是我的第一个 Mac 应用程序,这些说明清晰如泥。和他们鬼混了好几天了,仍然不知道我应该做什么。下面提到了扩展,但 sqlite 没有设置扩展。您只需使用您想要的任何扩展名(比方说 .sqlite)。有人可以解释我需要添加哪些属性到 info.plist 中吗?

\n\n
\n

相关项目:

\n\n

应用程序沙箱的相关项目功能允许您的应用程序访问与用户选择的文件同名但扩展名不同的文件。此功能由两部分组成:应用程序 Info.plist 文件中的相关扩展列表和告诉沙箱您正在做什么的代码。

\n\n

有两种常见情况可以说明这一点:

\n\n

场景1:(与我的问题无关)

\n\n

场景 2:您的应用需要能够打开或保存多个具有相同名称和不同扩展名的相关文件(例如,自动打开与电影文件同名的字幕文件,或者允许 SQLite 日志文件)。

\n\n

要访问该辅助文件,请创建一个符合 NSFilePresenter 协议的类。此对象应提供主\n 文件\xe2\x80\x99s URL 作为其primaryPresentedItemURL 属性,并应提供辅助文件\xe2\x80\x99s URL 作为其presentedItemURL 属性。

\n\n

用户打开主文件后,您的文件呈现器对象应该调用 NSFileCoordinator 类上的 addFilePresenter: 类方法来注册自身。

\n\n

注意: 对于 SQLite 日志文件,从 10.8.2 开始,如果您打开 SQLite 数据库,日志文件、预写日志文件和共享内存文件将自动添加到相关项目列表中,所以这一步是不必要的。

\n\n

在这两种情况下,您都必须对 application\xe2\x80\x99s\n Info.plist 文件进行少量更改。您的应用应该已经声明了一个文档类型\n (CFBundleDocumentTypes) 数组,用于声明您的应用\n 可以打开的文件类型。

\n\n

对于该数组中的每个文件类型字典,如果该文件类型应被视为潜在相关类型以用于打开和保存目的,则添加布尔值 YES 的键 NSIRelatedItemType。

\n
\n

小智 3

使用文档包

您的应用程序可以注册一种文档文件类型,该文档文件类型在 Finder 中显示为常规文件,但实际上是一个目录。当用户选择新位置来保存文件时,沙箱会授予您的应用程序将多个文件(包括 sqlite 的临时文件)写入用户选择的位置(以及您想要以该 URL 开头创建的任何子目录)的权限。 。

因此,如果用户选择 ~/Desktop/Untitled.appDocExtension 来保存文件,您的应用程序现在可以写入以下文件:~/Desktop/Untitled.appDocExtension/myFile1.sqlite以及~/Desktop/Untitled.appDocExtension/myFile1.sqlite-journal.

GarageBand (.band) 文件就是此类文档的一个示例。如果右键单击 Finder 中的文档并选择“显示包内容”,则可以看出 Finder 文件图标代表文档包。(当然,基本上就像一个应用程序包。)

如何设置:

  1. 将用户选择的文件读写权限添加到您的应用程序:com.apple.security.files.user-selected.read-write
  2. 将新的文档类型添加到应用程序的 Info.plist,包括唯一的文件扩展名,并确保添加密钥LSTypeIsPackage
<key>CFBundleDocumentTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeExtensions</key>
            <array>
                <string>appDocExtension</string>
            </array>
            <key>CFBundleTypeName</key>
            <string>MyDocTypeName</string>
            <key>CFBundleTypeRole</key>
            <string>Editor</string>
            <key>LSHandlerRank</key>
            <string>Default</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>com.mydomain.typeidentifier</string>
            </array>
            <key>LSTypeIsPackage</key>
            <true/>
        </dict>
    </array>
Run Code Online (Sandbox Code Playgroud)
  1. 将导出的类型声明添加到应用程序的 Info.plist(请参阅此处)。
<key>UTExportedTypeDeclarations</key>
    <array>
        <dict>
            <key>UTTypeConformsTo</key>
            <array>
                <string>com.apple.package</string>
                <string>public.composite-content</string>
            </array>
            <key>UTTypeDescription</key>
            <string>My Doc type description</string>
            <key>UTTypeIcons</key>
            <dict/>
            <key>UTTypeIdentifier</key>
            <string>com.mydomain.typeidentifier</string>
            <key>UTTypeTagSpecification</key>
            <dict>
                <key>public.filename-extension</key>
                <array>
                    <string>appDocExtension</string>
                </array>
            </dict>
        </dict>
    </array>
Run Code Online (Sandbox Code Playgroud)

要在运行时保存文档:

向用户呈现一个 NSSavePanel:

        let savePanel = NSSavePanel()
        savePanel.canCreateDirectories = true
        savePanel.allowedFileTypes = ["appDocExtension"]
        savePanel.begin { response in
            guard response == .OK else {return}
            guard let url = savePanel.url else {return}
            DispatchQueue.main.async {[weak self] in
                // this directory will look like a single file in Finder
                try! FileManager.default.createDirectory(at: url, withIntermediateDirectories: false)
                // create a location under this directory to save your sqlite db:
                let dbURL = url.appendingPathComponent("myFile1.sqlite")
                // ... and now you can open and write to the db at dbURL
            }
        }
Run Code Online (Sandbox Code Playgroud)

如果用户希望最终直接访问原始 sqlite 文件,他们可以:

  • 使用 Finder 中的“显示包内容”命令。
  • 或导航到终端中的文件。
  • 或使用您在应用程序中提供的导出命令将 .sqlite 文件复制到新位置。

您可以在这些包中存储您想要的任何内容,因此,如果您以后想要添加元数据或附属文件(例如图像或其他媒体),您可以将它们全部打包在一起。