SwiftUI local JSON 保存字符串值

SwiftUI local JSON save string value

我创建了一个本地 json。我更改了 json 中名称键的值,当我关闭并打开应用程序时,它再次显示“测试”。如何保存我对 Json 文件所做的更改?

为什么我不能保存字符串值?我和你分享了所有的代码。如果你想要我可以分享这个项目。

本地JSON文件

{
  "person": {
  "name": "Test"
  }
}

型号

struct PersonContainer: Codable {
    var person: Person?
}

struct Person: Codable {
    var name: String?
}

JSON 提供商

class JSONProvider: ObservableObject {
    
    @Published var personContainer: PersonContainer = PersonContainer()
    var fm = FileManager.default
    var fresult: Bool = false
    @Published var subUrl: URL? = URL(string: "")
    var mainUrl: URL? = Bundle.main.url(forResource: "test", withExtension: "json")
    
    func getData() {
        do {
            let documentDirectory = try fm.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
            subUrl = documentDirectory.appendingPathComponent("test.json")
            loadFile(mainPath: mainUrl!, subPath: subUrl!)
        } catch {
            print(error)
        }
    }
    
    func loadFile(mainPath: URL, subPath: URL){
        if fm.fileExists(atPath: subPath.path){
            decodeData(pathName: subPath)
            
            if ((personContainer.person) != nil) {
                decodeData(pathName: mainPath)
            }
            
        }else{
            decodeData(pathName: mainPath)
        }
    }
    
    func decodeData(pathName: URL){
        do{
            let jsonData = try Data(contentsOf: pathName)
            let decoder = JSONDecoder()
            let personContainer = try decoder.decode(PersonContainer.self, from: jsonData)
            self.personContainer = personContainer
            
        } catch {}
    }
    
    func writeToFile(location: URL) {
        do{
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted
            let JsonData = try encoder.encode(personContainer)
            try JsonData.write(to: location)
        } catch {
            
        }
    }
}

ContentView

struct ContentView: View {
    @State var text: String = ""
    @ObservedObject var jsonProvider: JSONProvider = JSONProvider()
    var body: some View {
        VStack {
            
            TextField("Placeholder", text: $text)
                .padding()
                .background(Color(UIColor.secondarySystemBackground))
                .cornerRadius(15)
                .padding(.horizontal)
                

            Text("Hello, world! \(jsonProvider.personContainer.person?.name ?? "")")
                .padding()
            
            Button(action: {
                jsonProvider.personContainer.person?.name = text
                jsonProvider.writeToFile(location: jsonProvider.subUrl!)
            }) {
                Text("Button")
            }
        }
        .onAppear {
            jsonProvider.getData()
        }
    }
}

看来您的方向是正确的,但还缺少一些东西。

由于原始主包的 test.json 只有在文档目录中的文件不存在时才应该加载,所以可以简化很多逻辑。例如,您可以删除 @Published subUrl,因为它永远不会更改并且不会被 View.

观察到

确保在按下按钮时调用 writeToFile

此外,在 catch 块中做一些 事情 (比如打印 error)总是一个好主意,以防出现问题。

class JSONProvider: ObservableObject {
    
    @Published var personContainer: PersonContainer = PersonContainer()
    private var fm = FileManager.default
    private let mainUrl: URL = Bundle.main.url(forResource: "test", withExtension: "json")!
    
    func getData() {
        if fm.fileExists(atPath: documentDirectoryJSONURL().path) {
            decodeData(fromURL: documentDirectoryJSONURL())
        } else {
            decodeData(fromURL: mainUrl)
        }
    }
    
    func documentDirectoryJSONURL() -> URL {
        do {
            let documentDirectory = try fm.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
            return documentDirectory.appendingPathComponent("test.json")
        } catch {
            fatalError("Couldn't create URL")
        }
    }
    
    func decodeData(fromURL url: URL){
        do{
            let jsonData = try Data(contentsOf: url)
            let decoder = JSONDecoder()
            let personContainer = try decoder.decode(PersonContainer.self, from: jsonData)
            self.personContainer = personContainer
        } catch {
            print(error)
            assertionFailure("Error decoding JSON")
        }
    }
    
    func writeToFile() {
        do{
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted
            let jsonData = try encoder.encode(personContainer)
            try jsonData.write(to: documentDirectoryJSONURL())
        } catch {
            print(error)
        }
    }
}

struct ContentView: View {
    @State var text: String = ""
    @ObservedObject var jsonProvider: JSONProvider = JSONProvider()
    var body: some View {
        VStack {
            
            TextField("Placeholder", text: $text)
                .padding()
                .background(Color(UIColor.secondarySystemBackground))
                .cornerRadius(15)
                .padding(.horizontal)
                

            Text("Hello, world! \(jsonProvider.personContainer.person?.name ?? "")")
                .padding()
            
            Button(action: {
                jsonProvider.personContainer.person?.name = text
                jsonProvider.writeToFile()
            }) {
                Text("Write")
            }
        }
        .onAppear {
            jsonProvider.getData()
        }
    }
}