最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

swift - Loading and saving JSON files - Stack Overflow

programmeradmin1浏览0评论

I want to write data to a custom JSON file, but it doesn't seem to load properly. The save function seems to work partially when I call it, but it looks like either it rewrites the file with only the newly saved data rather than the whole array of the json data + the new one, or there is a problem in my load function.

func loadCustomCadences(filename fileName: String) -> [Cadence] {
    var cadences = [Cadence]()
    if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
        do {
            let data = try Data(contentsOf: url)
            cadences = try JSONDecoder().decode([Cadence].self, from: data)
            return cadences
        } catch {
            print("error:\(error)")
        }
    }
    return cadences
}

func saveCadences(_ cadence: Cadence) {
    loadedCadences = loadCustomCadences(filename: "customCadences")
    print("loa", loadedCadences)

    do {
        loadedCadences.append(cadence)
        print("loazeaz", loadedCadences)
        let data = try JSONEncoder().encode(loadedCadences)
        let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let fileURL = documentsDirectory.appendingPathComponent("customCadences.json")
        try data.write(to: fileURL)
        let jsonF = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
        print(jsonF ?? "zerzer")
        
    } catch {
        print("Error saving JSON: \(error)")
    }
    
}

The first print always returns an empty array despite loading from the same file I've just saved to, while the others respectively return the data I'm trying to save and its json entry, but nothing is ever appended when I'm saving the new data, it only loads and empty array and rewrites the file with only the latest data.

Any idea how to solve this ?

I want to write data to a custom JSON file, but it doesn't seem to load properly. The save function seems to work partially when I call it, but it looks like either it rewrites the file with only the newly saved data rather than the whole array of the json data + the new one, or there is a problem in my load function.

func loadCustomCadences(filename fileName: String) -> [Cadence] {
    var cadences = [Cadence]()
    if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
        do {
            let data = try Data(contentsOf: url)
            cadences = try JSONDecoder().decode([Cadence].self, from: data)
            return cadences
        } catch {
            print("error:\(error)")
        }
    }
    return cadences
}

func saveCadences(_ cadence: Cadence) {
    loadedCadences = loadCustomCadences(filename: "customCadences")
    print("loa", loadedCadences)

    do {
        loadedCadences.append(cadence)
        print("loazeaz", loadedCadences)
        let data = try JSONEncoder().encode(loadedCadences)
        let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let fileURL = documentsDirectory.appendingPathComponent("customCadences.json")
        try data.write(to: fileURL)
        let jsonF = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
        print(jsonF ?? "zerzer")
        
    } catch {
        print("Error saving JSON: \(error)")
    }
    
}

The first print always returns an empty array despite loading from the same file I've just saved to, while the others respectively return the data I'm trying to save and its json entry, but nothing is ever appended when I'm saving the new data, it only loads and empty array and rewrites the file with only the latest data.

Any idea how to solve this ?

Share Improve this question asked Mar 13 at 2:42 JulienMJulienM 491 gold badge3 silver badges11 bronze badges 6
  • So, do you have 2 different JSON files here? The first one is on your project directory and the second one is stored within . documentDirectory, right? – sonle Commented Mar 13 at 3:19
  • @sonle Do you mean I have to change Bundle.main in the load function ? – JulienM Commented Mar 13 at 3:36
  • From my understanding, you're trying to load a hard copy of a JSON file from local (the file that you can see visually on your project directory). Then appending a few more elements and saving it to .documentDirectory. Is it correct? – sonle Commented Mar 13 at 3:40
  • @sonle it's not what I intended to do, I was following a tutorial to write and save a json file but I can't seem to find a way to load and save them from the same directory, how would I do that ? If I replace the url of the load function with the url of the save function I get this error : "Initializer for conditional binding must have Optional type, not 'URL'" – JulienM Commented Mar 13 at 3:48
  • 1 In loadCustomCadences instead of using if let url = Bundle.main.url(forResource: fileName, withExtension: "json") use the same directory you use for saving the data, that is inside the do, let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileURL = documentsDirectory.appendingPathComponent("customCadences.json"). Note no if let. – workingdog support Ukraine Commented Mar 13 at 4:29
 |  Show 1 more comment

2 Answers 2

Reset to default 1

Your assumption in "The first print always returns an empty array despite loading from the same file I've just saved to, ..." is not correct. You are reading from a file in the Bundle.main.url(forResource: ...), but you are writing to a different file, let fileURL = documentsDirectory.appendingPathComponent("customCadences.json").

To read and write to the same file, try this approach using the same file and directory you save the json data to:


    func loadCustomCadences(filename fileName: String) -> [Cadence] {
        var cadences = [Cadence]()
        do {
            let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
            let fileURL = documentsDirectory.appendingPathComponent("customCadences.json")
            let data = try Data(contentsOf: fileURL)
            print("---> data: \(String(data: data, encoding: .utf8) as AnyObject)")
            cadences = try JSONDecoder().decode([Cadence].self, from: data)
            print("---> cadences: \(cadences)")
            return cadences
        } catch {
            print("error:\(error)")
        }
        return cadences
    }


    func saveCadences(_ cadence: Cadence) {
        var loadedCadences = loadCustomCadences(filename: "customCadences")
        print("---> loa", loadedCadences)
        do {
            loadedCadences.append(cadence)
            print("loazeaz", loadedCadences)
            let data = try JSONEncoder().encode(loadedCadences)
            let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
            let fileURL = documentsDirectory.appendingPathComponent("customCadences.json")
            try data.write(to: fileURL)
            let jsonF = String(data: data, encoding: .utf8)
            print("---> jsonF: \(jsonF)")
        } catch {
            print("Error saving JSON: \(error)")
        }
    }

The main reason is you're confusing between:

  • Bundle.main.url: load the resources from your app bundle (your project folder)
  • FileManager.default: handle files and directories, even with iCloud.

And from your comments, I believe should use FileManager.default only. I could refactor the code as below:

    //Define the directory
    private var cadencesDirectory: URL? {
        FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
            .first?
            .appendingPathComponent("customCadences")
            .appendingPathExtension("json")
    }
    
    //Check whenever need to create the directory
    private func checkValidDirectoryAndCreateIfNeeded() {
        guard let cadencesDirectory else { return }
        guard !FileManager.default.fileExists(atPath: cadencesDirectory.path) else { return }
        do {
            try FileManager.default.createDirectory(at: cadencesDirectory, withIntermediateDirectories: true)
        } catch {
            print("Cannot create the directory")
        }
    }
    
    func load() -> [Cadence] {
        checkValidDirectoryAndCreateIfNeeded()
        if let path = cadencesDirectory?.path {
            let data = FileManager.default.contents(atPath: path)
            //TODO: JSONDecoder or whatever here
        }
    }

    func save() {
        ...
        checkValidDirectoryAndCreateIfNeeded()
        if let cadencesDirectory {
            ...
            data.write(to: cadencesDirectory)
        }
    }
发布评论

评论列表(0)

  1. 暂无评论