SwiftUI update/replace 键,JSON 文件中的值

SwiftUI update/replace key, value in a JSON file

我是 SwiftUI 编码的新手。我有一个具有以下数据结构的 JSON 文件。尝试在按下按钮时使用 TextField 变量更新特定键的值。我能够读取 JSON 文件内容,但无法弄清楚如何更新特定密钥并写回文件。这是用于测试 MacOS 应用程序的。请帮我。提前致谢。

\settingsData.json
[
    {
        "id" : 1001,
        "username": "example",
        "password": "123456",
    }
]
import SwiftUI
import Foundation

var jsonArray: [jsonModel] = readJSON("settingsData")

struct testJSON: View {
    @State private var UserName: String = ""
    
    var body: some View {
        VStack (alignment: .leading) {
          
            ForEach(jsonArray) { jsonValue in
                Text("Username is \(jsonValue.username)")
            }

            TextField("Username", text: $UserName)
            
            Button(action: {
                // Update array and save to file
                print("Done")
            })
            {
                    Text("Save")
            }
        }
    }
}

这是模型

struct jsonModel: Hashable, Codable, Identifiable {
    var id: Int
    var username: String
    var password: String
}

这是我用来从 App 目录中的文件读取 JSON 数据的简单代码。

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

这是一些代码,向您展示如何 在 macOS 上读取和写入文件(给予正确的权限)。

有了这个你应该可以编写代码来 “...更新特定密钥并写回 a 文件...”。

正如我在评论中提到的,应用程序包是只读的。 因此,您必须使用 FileManager 将更新写入“documentDirectory”(例如)。

import Foundation
import SwiftUI

@main
struct MacApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
 
struct ContentView: View {
    @StateObject var dataManager = DataManager()
    @State private var userName: String = ""
    
    var body: some View {
        VStack (alignment: .leading) {
            
            ForEach(dataManager.jsonArray) { jsonValue in
                Text("Username is \(jsonValue.username)")
            }
            
            TextField("Username", text: $userName)
            
            Button(action: {
                // Update array, just for testing update all userName
                for i in dataManager.jsonArray.indices {
                    dataManager.jsonArray[i].username = userName
                }
                // save to file
                dataManager.saveToFile()
                print("Done")
            })
            {
                Text("Save")
            }
        }.frame(width: 444, height: 444)
    }
    
}

class DataManager: ObservableObject {
    @Published var jsonArray: [JsonModel] = []
    
    var fileName = "settingsData"
    
    init() {
        readFromFile()
    }
    
    func readFromFile() {
        jsonArray = readJSONBundle(fileName) // do this only the first time you open the app
      //  readJSON() // read from the saved file
    }
    
    private func readJSONBundle<T: Decodable>(_ filename: String) -> T {
        let data: Data
        
        guard let file = Bundle.main.url(forResource: filename, withExtension: "json")
        else {
            fatalError("Couldn't find \(filename) in main bundle.")
        }
        do {
            print(file)
            data = try Data(contentsOf: file)
        } catch {
            fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
        }
        do {
            let decoder = JSONDecoder()
            let jsonData = try decoder.decode(T.self, from: data)
            return jsonData
        } catch {
            fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
        }
    }

    func saveToFile() {
        do {
            let furl = try FileManager.default
                .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
                .appendingPathComponent(fileName)
                .appendingPathExtension("json")
            print("---> writing to: \(furl)")
            let data = try JSONEncoder().encode(jsonArray)
            try data.write(to: furl)
        } catch {
            print("---> error saveToFile: \(error)")
        }
    }
    
    func readJSON() {
        do {
            let furl = try FileManager.default
                .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
                .appendingPathComponent(fileName)
                .appendingPathExtension("json")
            print("---> reading from: \(furl)")
            let data = try Data(contentsOf: furl)
            let decoder = JSONDecoder()
            let jsonData = try decoder.decode([JsonModel].self, from: data)
            jsonArray = jsonData
        } catch {
            print("---> error reading from file: \(error)")
        }
    }
    
}

struct JsonModel: Hashable, Codable, Identifiable {
    var id: Int
    var username: String
    var password: String
}