Hello, I'm trying to save data in my app to a JSON file and I'm completely stuck. All of the articles I've found online are way to high level and over explained everything so it's got me all confused.
I have a model setup for a Person:
struct Person: Identifiable, Hashable {
let id: Int
let name: String
}
Inside the action of a button I have the model stored in a variable called person and I want to basically add this data to the top of a JSON object called people.json.
So the idea is that I have the following that already exists in my JSON file:
Then, convert the JSON data into Swift objects (an array of Person structs). + add Codable protocol compliance to Person struct.
Add your new Person object to the beginning of this array.
Convert the updated array back to JSON data.
Finally, write the updated JSON data back to the file.
func savePeople(_ people: [Person]) {
do {
let data = try JSONEncoder().encode(people)
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsDirectory.appendingPathComponent("people.json")
try data.write(to: fileURL)
} catch {
print("Error saving JSON: \(error)")
}
}
Use these functions in your button action:
@State private var people: [Person] = []
var body: some View {
Button("Add David") {
let newPerson = Person(id: 4, name: "David")
people.insert(newPerson, at: 0) // Add to the top of the array
savePeople(people)
}
.onAppear {
people = loadPeople()
}
}
To read the updated data:
Button("Read Updated Data") {
people = loadPeople()
print(people)
}
Note: This approach saves the file in the app's documents directory, which is writable. The original file in your app bundle is read-only.
Was just about to post a response but @BravoSierra beat me to it.
Here's a compact version of the above:
func jsonExample() throws {
guard let bundleUrl = Bundle.main.url(forResource: "persons", withExtension: "json") else { return }
let documentsUrl = URL.documentsDirectory.appendingPathComponent("persons.json")
let data = try Data(contentsOf: bundleUrl)
var persons = try JSONDecoder().decode([Person].self, from: data)
persons.insert(Person(id: 4, name: "David"), at: 0)
try JSONEncoder().encode(persons).write(to: documentsUrl)
let stored = try Data(contentsOf: documentsUrl)
let decoded = try JSONDecoder().decode([Person].self, from: stored)
print(decoded)
}
If you're dealing with a large array, you may want to do something more sophisticated than inserting the new item at the beginning, as doing so isn't overly efficient.
The answer above is what I would suggest. One addition is you would have to write it back to the same file using Bundle.main, which I think the answers above missed.