JSONencoder不保存新的编码JSON数组iOS

问题描述

我有一个看起来像这样的json文件(在名为list.json的文件中)

[
  {
    "id": "C8B046E9-70F5-40D4-B19A-40B3E0E0877B","name": "Dune","author": "Frank Herbert","page": "77","total": "420","image": "image1.jpg"
  },{
    "id": "2E27CA7C-ED1A-48C2-9B01-A122038EB67A","name": "Ready Player One","author": "Ernest Cline","page": "234","image": "image1.jpg"
  }
]

这是我的应用随附的默认文件(这些示例可以删除)。我的内容视图具有一个成员变量,该成员变量使用我编写的解码函数来获取json数组并将其显示在列表中。我有一个观点,将另一本书添加到json文件中。视图将另一个结构附加到数组,然后使用此功能将新附加的数组编码为list.json

func writeJSON(_ bookData: [Book]) {
    do {
        let fileURL = try FileManager.default.url(for: .documentDirectory,in: .userDomainMask,appropriateFor: nil,create: true)
            .appendingPathComponent("list.json")

        let encoder = JSONEncoder()
        try encoder.encode(bookData).write(to: fileURL)
    } catch {
        print(error.localizedDescription)
    }
}

当按下按钮时,在NewBook视图中调用此函数。 bookData是内容视图中的解码数组,我在NewBook视图中使用了Binding。

如果您添加书籍并返回到contentview(该列表现在包含附加的结构),则该代码有效,但是如果您关闭该应用程序并再次打开它,则该列表将使用默认的json文件。我认为我的writeJSON函数存在错误。

还请注意,我尝试将URL中的create参数更改为false,但这无济于事。

编辑:我正在按要求添加Book结构

struct Book: Hashable,Codable,Identifiable {
    var id: UUID
    var name: String
    var author: String
    var page: String
    var total: String
    var image: String
}

编辑2:这是针对iOS应用

编辑3:我的负载数据功能

func load<T: Decodable>(_ filename: String) -> T {
    let data: Data
    guard let file = Bundle.main.url(forResource: filename,withExtension: nil)
        else {
            fatalError("Couldn't find \(filename) in main bundle.")
    }
    
    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
    }
    
    do {
        let decoder = JSONDecoder()
        return try decoder.decode(T.self,from: data)
    } catch {
        fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
    }
}

解决方法

您可能不会覆盖磁盘上的现有文件。将数据写入磁盘时,尝试options: .atomic

func writeJSON(_ bookData: [Book]) {
    do {
        let fileURL = try FileManager.default.url(for: .documentDirectory,in: .userDomainMask,appropriateFor: nil,create: true).appendingPathComponent("list.json")
        try JSONEncoder().encode(bookData).write(to: fileURL,options: .atomic)
    } catch {
        print(error)
    }
}

编辑/更新:

这里的问题是您没有将文件保存在您认为的位置。 Bundle目录为只读目录,与您的App文档目录无关。

func load<T: Decodable>(_ filename: String) -> T? {
    // no problem to force unwrap here you actually do want it to crash if the file it is not inside your bundle
    let readURL = Bundle.main.url(forResource: filename,withExtension: "json")!
    let documentDirectory = FileManager.default.urls(for: .documentDirectory,in: .userDomainMask).first!
    let jsonURL = documentDirectory
        .appendingPathComponent(filename)
        .appendingPathExtension("json")
    // check if the file has been already copied from the Bundle to the documents directory
    if !FileManager.default.fileExists(atPath: jsonURL.path) {
        // if not copy it to the documents (not it is not a read-only anymore)
        try? FileManager.default.copyItem(at: readURL,to: jsonURL)
    }
    // read your json from the documents directory to make sure you get the latest version
    return try? JSONDecoder().decode(T.self,from: Data(contentsOf: jsonURL))
}
func writeJSON(_ bookData: [Book]) {
    let documentDirectory = FileManager.default.urls(for: .documentDirectory,in: .userDomainMask).first!
    let jsonURL = documentDirectory
        .appendingPathComponent("list")
        .appendingPathExtension("json")
    // write your json at the documents directory and use atomic option to override any existing file
    try? JSONEncoder().encode(bookData).write(to: jsonURL,options: .atomic)
}

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...