You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中FileManager.default.url无法创建文件报错的问题排查

Swift本地文件存储与加载问题解决

原实现代码

保存函数

func locallySave(_ data: Data?, for utility: Utility) throws {
    var fileURL = try FileManager.default.url(for: .applicationSupportDirectory,
                                               in: .userDomainMask,
                                               appropriateFor: nil,
                                               create: true)
    fileURL.appendPathComponent(Constants.appShortName)
    fileURL.appendPathComponent("\(utility).json")
    do {
        try data?.write(to: fileURL)
    } catch {
        print("Error: \(error)")
        throw error
    }
}

加载函数

func locallyLoad(_ utility: Utility) throws -> Data? {
    var fileURL = try FileManager.default.url(for: .applicationSupportDirectory,
                                               in: .userDomainMask,
                                               appropriateFor: nil,
                                               create: false)
    fileURL.appendPathComponent(Constants.appShortName)
    fileURL.appendPathComponent("\(utility).json")
    do {
        let data = try Data(contentsOf: fileURL)
        return data
    } catch {
        print("Error: \(error)")
        throw error
    }
}

问题说明

当前遇到的核心问题:

  • 保存函数设置create: true后,仍未自动创建所需的子目录及文件,实际路径无内容,报错NSUnderlyingError=0x600000fb0480 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}
  • 怀疑模拟器中每次运行应用的目录变化导致读取失败,但调试时两函数生成的fileURL完全一致

修复与优化方案

1. 自动创建缺失的目录

url(for:in:appropriateFor:create:)的create: true仅会创建顶层的.applicationSupportDirectory,不会自动创建后续追加的子目录(即Constants.appShortName对应的文件夹)。需手动添加目录创建逻辑:

修改后的保存函数:

func locallySave(_ data: Data?, for utility: Utility) throws {
    guard let data = data else { return } // 空数据直接返回,避免无意义操作
    var fileURL = try FileManager.default.url(for: .applicationSupportDirectory,
                                               in: .userDomainMask,
                                               appropriateFor: nil,
                                               create: true)
    fileURL.appendPathComponent(Constants.appShortName)
    // 创建子目录(含中间目录,确保路径上所有缺失的文件夹都被创建)
    try FileManager.default.createDirectory(at: fileURL, withIntermediateDirectories: true)
    fileURL.appendPathComponent("\(utility).json")
    try data.write(to: fileURL)
}

2. 解决模拟器目录一致性误解

模拟器中applicationSupportDirectory的路径不会随每次运行变化——路径的唯一标识是应用的Bundle ID,只要Bundle ID不变,目录就固定。之前的读取失败本质还是目录未创建导致的,而非路径变化。

可以在加载函数中添加文件存在判断,做更友好的容错处理:

func locallyLoad(_ utility: Utility) throws -> Data? {
    var fileURL = try FileManager.default.url(for: .applicationSupportDirectory,
                                               in: .userDomainMask,
                                               appropriateFor: nil,
                                               create: false)
    fileURL.appendPathComponent(Constants.appShortName)
    fileURL.appendPathComponent("\(utility).json")
    
    // 先判断文件是否存在,不存在直接返回nil
    guard FileManager.default.fileExists(atPath: fileURL.path) else {
        return nil
    }
    return try Data(contentsOf: fileURL)
}

3. 提取公共路径逻辑(可选优化)

为避免重复代码,可将路径构建逻辑提取为私有方法:

private func fileURL(for utility: Utility) throws -> URL {
    var fileURL = try FileManager.default.url(for: .applicationSupportDirectory,
                                               in: .userDomainMask,
                                               appropriateFor: nil,
                                               create: true)
    fileURL.appendPathComponent(Constants.appShortName)
    try FileManager.default.createDirectory(at: fileURL, withIntermediateDirectories: true)
    fileURL.appendPathComponent("\(utility).json")
    return fileURL
}

简化后的保存与加载函数:

func locallySave(_ data: Data?, for utility: Utility) throws {
    guard let data = data else { return }
    try data.write(to: fileURL(for: utility))
}

func locallyLoad(_ utility: Utility) throws -> Data? {
    let url = try fileURL(for: utility)
    guard FileManager.default.fileExists(atPath: url.path) else {
        return nil
    }
    return try Data(contentsOf: url)
}

内容的提问来源于stack exchange,提问作者Tiny Tim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 20:20:18