Bundle.main.paths(forResourcesOfType:inDirectory) 在iOS上不起作用

移动开发 2026-07-10

我正在创建一个多平台的SwiftUI应用。

经过一番初期的摸索之后,我终于创建了一个文件目录并把它们加入到应用的bundle。在启动时,我尝试使用Bundle的 Bundle.main.paths(forResourcesOfType:) 函数来查找这些文件并打开它们。

Mac版本的应用能够把文件找到得很顺利。将同样的代码在iOS版本上运行时,返回的是一个空的文件路径数组。

我可以在Finder中定位到模拟器版本应用的bundle,使用“显示包内容”打开它,bundle中的文件夹清晰可见、里面的文件也写好了。然而,Bundle.main.paths(forResourcesOfType:) 返回一个空列表。

我最初尝试获取bundle的资源路径,将我的文件夹名追加到该路径,并用FileManager来获取文件列表,但失败了,于是改用Bundle方法。

这两种方法在macOS上都能工作,在iOS上都失败。

是不是有什么我没注意到的沙箱问题?

以下是我的代码:

    static var scopeTemplates: [ScopeTemplate] = {
        let fileManager = FileManager.default
        let folderName = "Kaleidoscope_Templates"
        do {
            if let bundlePath = Bundle.main.resourcePath {
                print("bundlePath = \(bundlePath)")
            } else {
                print("Can't resolve bundle path")
            }
            // old path-based code.
            //let kaleidoscopeTemplateFileNames = Bundle.main.paths(forResourcesOfType: "json", inDirectory: folderName)
            //let kaleidoscopeTemplateURLs = kaleidoscopeTemplateFileNames.map { URL(filePath: $0) }
            guard let kaleidoscopeTemplateURLs = Bundle.main.urls(forResourcesWithExtension: "json", subdirectory: folderName) else {
                fatalError("Can't find folder \(folderName)")
            }



            let decoder = JSONDecoder()
            var scopeTemplates = [ScopeTemplate]()
            for aFileURL in kaleidoscopeTemplateURLs {
                print("filename = \(aFileURL.path)")
                let data = try Data(contentsOf: aFileURL)
                let aScopeTemplate = try decoder.decode(ScopeTemplate.self, from: data)
                scopeTemplates.append(aScopeTemplate)
            }
            return scopeTemplates.sorted() { lhv, rhv in
                return lhv.index < rhv.index

            }
        } catch {
            fatalError("Error \(error) reading template files")
        }
    }()

(这一版代码不会崩溃,也不会记录错误,但返回的是空数组。尝试在iOS上用FileManager获取该目录中的文件时会抛出异常,不过该版本在macOS上也能工作。)

另外请注意,iOS与 macOS的 bundle结构看起来完全相同。我在模拟器中用Finder的“前往文件夹...”菜单命令打开bundle中的目录,看到的结构和预期完全一致。

我刚刚创建了一个最小可复现的项目并在Github上发布:

https://github.com/DuncanMC/BundleFiles.git

那段代码是这样的:

    static var scopeTemplates: [ScopeTemplate] = {

        let fileManager = FileManager.default
        let folderName = "Kaleidoscope_Templates"
        do {
            if let bundlePath = Bundle.main.resourcePath {
                print("bundlePath = \(bundlePath)")
            } else {
                print("Can't resolve bundle path")
            }
            guard let kaleidoscopeTemplateURLs = Bundle.main.urls(forResourcesWithExtension: "json", subdirectory: folderName) else {
                fatalError("Can't find folder \(folderName)")
            }

            print("Directory \(folderName) in bundle resources contains \(kaleidoscopeTemplateURLs.count) files.")

            let decoder = JSONDecoder()
            var scopeTemplates = [ScopeTemplate]()
            for aFileURL in kaleidoscopeTemplateURLs {
                print("filename = \(aFileURL.path)")
                let data = try Data(contentsOf: aFileURL)
                let aScopeTemplate = try decoder.decode(ScopeTemplate.self, from: data)
                scopeTemplates.append(aScopeTemplate)
            }
            return scopeTemplates.sorted() { lhv, rhv in
                return lhv.index < rhv.index

            }
        } catch {
            fatalError("Error \(error) reading template files")
        }
    }()

(Github上的项目包含Xcode工程、充满文件的文件夹等。它被设置为一个SwiftUI多平台项目,与我现在努力解决的真实应用程序一样。)

在iOS模拟器中运行时,它会按预期记录bundle路径:

bundlePath = /Users/duncan/Library/Developer/CoreSimulator/Devices/EABDE8A8-0CAC-4D5F-81EF-FB03DC809EE5/data/Containers/Bundle/Application/8EA76044-8F14-4783-A45B-423A35F1EB4F/BundleFiles.app

然后输出

Directory Kaleidoscope_Templates in bundle resources contains 0 files.

在我的Mac上运行同样的代码时,会加载并解码bundle中的4 个文件,正如应当的那样。

在Mac上运行时,bundle路径以Contents/Resources结尾;而在iOS上,路径是应用程序bundle的顶层目录。(我相信这是预期的行为。)

解决方案

问题解决,我感觉自己像个白痴。我的项目中有一个名为 "Kaleidoscope_templates" 的文件夹,但代码却试图使用一个名为 "Kaleidoscope_Templates"(大写字母T)的文件夹。

Matt增补了一个关于文件夹名大小写不匹配的回答,然后又删除了。这个不匹配其实是不同的,但确实是原因。

我现在想起来,macOS的文件系统不是区分大小写的,但iOS是大小写敏感的。

@matt,如果你能把回答找回来我就能接受。谢谢!我盯着那段愚蠢的代码看得太久了。(我的大脑对大小写不敏感,所以很容易错过这一点,但本该三次检查它。)

再一次感谢!

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章