Reputation: 1480
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.
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
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