Ufuk Köşker
Ufuk Köşker

Reputation: 1480

SwiftUI local JSON save string value

I created a local json. I change the value of the name key in the json and when I close and open the application, it says "Test" again. How can I save the change I made on the Json file?

Why can't I save the string value? I shared all the codes with you. If you want I can share the project.

enter image description here

Local JSON File

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

Model

struct PersonContainer: Codable {
    var person: Person?
}

struct Person: Codable {
    var name: String?
}

JSON Provider

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()
        }
    }
}

Upvotes: 1

Views: 1643

Answers (1)

jnpdx
jnpdx

Reputation: 52615

Looks like you were on the right track, but there were a few things missing.

Since the original main bundle's test.json should only be loaded if the file in the documents directory doesn't exist, a lot of the logic can be simplified. For example, you can remove the @Published subUrl, since it never gets changed and isn't observed by the View.

Make sure that you call the writeToFile when the button is pressed.

Also, it's always a good idea to do something (like printing the error) inside the catch blocks in case something has gone wrong.

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()
        }
    }
}

Upvotes: 2

Related Questions