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

tzxcd3kk  于 2023-01-13  发布在  SQLite
关注(0)|答案(1)|浏览(163)

我有一个MacOS应用程序,我想把它放在Mac App Store上,所以它必须是沙箱。使用该应用程序,用户创建多个SQLite数据库文件来存储他们创建的信息,有点类似于Excel电子表格文件,但我在我的应用程序中利用SQLite来创建、读取和编辑文件。他们将这些文件存储在Mac上的Documents文件夹中。我为此添加了一个权限:apple.security.files.user-selected. read-write.这个应用程序可以很好地读取这些数据库文件,但是如果我试图写入它们,它就失败了。
根据Mac控制台应用程序的日志,错误为:
沙盒:MyApp帮助(16378)拒绝(1)文件写入创建/用户/steve/文档/myFile1. sqlite-journal
违规:拒绝(1)文件写入创建/用户/steve/文档/myFile1. sqlite-journal
失败的原因是SQLite在后台创建了一个与被访问文件同名的临时文件,但通过在其后面添加"-journal"更改了扩展名。因此,如果用户打开名为myFile1.sqlite的文件,它会打开并正常读取,但如果用户尝试写入该文件,SQLite将创建一个名为myFile1.sqlite-journal的临时文件,作为该过程的一部分。但由于用户没有打开或保存名为myFile1.sqlite-journal的文件,因此该文件不在沙箱中,并被拒绝。
我通过在Finder中创建一个名为myFile1.sqlite-journal的空文件并从我的应用程序中打开它(从而将其作为用户选择的文件添加到沙箱中),然后能够写入myFile1.sqlite,从而确认了这是问题所在。
这是一个已知问题,根据文档,似乎可以通过使用"相关项目"来解决:https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html.
下面是相关的文字,但这是我的第一个mac应用程序,这些说明是明确的泥。我已经摆弄了几天,仍然不知道我应该做什么。下面提到的扩展名,但sqlite没有一个固定的扩展名。你只是使用任何扩展名你想要的(让我们说. sqlite)。谁能解释一下,我需要添加到info.plist的属性。

    • 相关项目:**

应用沙盒的相关项目功能允许你的应用访问与用户选择的文件同名但扩展名不同的文件。此功能由两部分组成:应用程序Info.plist文件中的相关扩展名列表,以及告诉沙箱您正在执行的操作的代码。
有两种常见的情况可以说明这一点:
情景1:(与我的问题无关)
场景2:您的应用需要能够打开或保存多个具有相同名称但不同扩展名的相关文件(例如,自动打开与电影文件同名的字幕文件,或允许SQLite日志文件)。
若要访问该辅助文件,请创建符合NSFilePresenter协议的类。此对象应提供主文件的URL作为其primaryPresentedItemURL属性,并应提供辅助文件的URL作为其presentedItemURL属性。
用户打开主文件后,文件演示器对象应调用addFilePresenter:NSFileCoordinator类上的类方法注册自身。

    • 注意:对于SQLite日志文件,从10.8.2开始,如果您打开SQLite数据库,日志文件、预写日志文件和共享内存文件会自动添加到相关项目列表中,因此无需执行此步骤。**

在这两种情况下,您都必须对应用的Info.plist文件进行少量更改。您的应用应该已经声明了一个文档类型(CFBundleDocumentTypes)数组,该数组声明了您的应用可以打开的文件类型。
对于该数组中的每个文件类型字典,如果出于打开和保存目的,应将该文件类型视为可能相关的类型,请添加具有布尔值YES的键NSIsRelatedItemType。

gj3fmq9x

gj3fmq9x1#

使用文档包

您的应用可以注册一个文档文件类型,该类型在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
1.向应用的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>

1.将导出的类型声明添加到应用的Info.plist(credit here)。

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

要在运行时保存文档:

向用户显示NSavePanel:

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

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

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

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

相关问题