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

Swift本地JSON文件更新后原文件未变更,求助原因

Swift中更新本地JSON文件不生效的原因及解决办法

问题背景

你在Swift项目的Data文件夹中创建了data.json作为初始数据,成功实现了从Bundle读取JSON并赋值到全局变量,但当更新全局变量后调用saveJSON方法,项目中的data.json始终没有变化,即使修改保存文件名也无效。

初始JSON内容:

{
    "fullname": "Testuser2",
    "answered": 0,
    "correct": 0
}

核心代码包含UserData结构体、全局变量定义、JSON读取方法及保存方法。


问题原因

1. Bundle内文件只读,保存路径并非项目目录

你读取的是Bundle.main中的data.json,但iOS APP的主Bundle是只读目录,安装后无法修改其中的文件。而你的saveJSON方法是把数据保存到了设备的Documents目录,并非项目中的Data文件夹,所以Xcode里看到的项目文件自然不会有变化。

2. 保存的JSON结构与原文件不一致

你将UserData包装成数组[UserData]进行编码,但原JSON是单个对象结构,编码后的JSON为数组格式,后续读取会触发解码错误,且不符合初始数据结构。

3. 读取逻辑未适配保存路径

你的readLocalFile始终从Bundle读取文件,即使成功保存到Documents目录,下次读取还是会加载Bundle里的初始数据,修改后的数据无法被复用。


解决步骤及修正代码

1. 修正保存逻辑:保存单个对象,输出错误信息

修改saveJSON方法,直接编码单个UserData对象,添加错误打印和保存路径输出:

func saveJSON() {
    print("---------")
    print(GlobalVariable.fullname)
    print(GlobalVariable.answered)
    print(GlobalVariable.correct)
    print("---------")
    
    // 直接使用单个UserData对象,而非数组
    let dataToUpdate = UserData(fullname: GlobalVariable.fullname, answered: GlobalVariable.answered, correct: GlobalVariable.correct)
    
    do {
        let jsonEncoder = JSONEncoder()
        jsonEncoder.outputFormatting = .prettyPrinted // 格式化JSON,方便查看
        let jsonData = try jsonEncoder.encode(dataToUpdate)
        
        guard let jsonString = String(data: jsonData, encoding: .utf8) else {
            print("Failed to convert JSON data to string")
            return
        }
        print(jsonString)
        
        guard let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
            print("Failed to access document directory")
            return
        }
        let savePath = documentDirectory.appendingPathComponent("data.json")
        try jsonString.write(to: savePath, atomically: true, encoding: .utf8)
        print("Data saved successfully to: \(savePath)")
    } catch {
        print("Save error: \(error.localizedDescription)")
    }
}

2. 修正读取逻辑:优先读取Documents目录的文件

修改readLocalFile方法,先尝试读取Documents目录中保存的文件,若不存在再加载Bundle里的初始数据:

private func readLocalFile(forName name: String) -> Data? {
    // 优先读取Documents目录的文件
    let documentDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    let docFileURL = documentDir.appendingPathComponent("\(name).json")
    
    if FileManager.default.fileExists(atPath: docFileURL.path) {
        do {
            return try Data(contentsOf: docFileURL)
        } catch {
            print("Error reading from documents: \(error)")
        }
    }
    
    // 若Documents无文件,读取Bundle中的初始数据
    do {
        guard let bundlePath = Bundle.main.path(forResource: name, ofType: "json"),
              let jsonData = try String(contentsOfFile: bundlePath).data(using: .utf8) else {
            return nil
        }
        return jsonData
    } catch {
        print("Error reading from bundle: \(error)")
    }
    return nil
}

3. 优化建议:避免使用全局变量

全局变量不利于数据维护和状态管理,建议改用单例类管理用户数据:

class UserDataManager {
    static let shared = UserDataManager()
    private(set) var userData: UserData?
    
    private init() {}
    
    func loadUserData() {
        guard let data = readLocalFile(forName: "data") else { return }
        do {
            userData = try JSONDecoder().decode(UserData.self, from: data)
        } catch {
            print("Decode error: \(error)")
        }
    }
    
    func updateUserData(answered: Int, correct: Int) {
        guard let currentData = userData else { return }
        // 若要直接修改属性,需将UserData的属性改为var
        userData = UserData(fullname: currentData.fullname, answered: answered, correct: correct)
        saveUserData()
    }
    
    private func saveUserData() {
        guard let data = userData else { return }
        do {
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted
            let jsonData = try encoder.encode(data)
            guard let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
            let savePath = docDir.appendingPathComponent("data.json")
            try jsonData.write(to: savePath)
        } catch {
            print("Save error: \(error)")
        }
    }
    
    // 复用readLocalFile方法
    private func readLocalFile(forName name: String) -> Data? {
        // 同之前的实现
    }
}

验证保存结果

你可以通过Xcode的「Devices and Simulators」查看模拟器的Documents目录,验证数据是否正确保存:

  1. 打开Xcode,点击菜单「Window」→「Devices and Simulators」
  2. 选择对应的模拟器和APP,点击「Download Container」
  3. 右键下载的容器文件,选择「Show Package Contents」,进入「AppData/Documents」即可看到保存的文件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 07:15:51