I want to write data to a custom JSON file, but it doesn't seem to load properly. The save function seems to work partially when I call it, but it looks like either it rewrites the file with only the newly saved data rather than the whole array of the json data + the new one, or there is a problem in my load function.
func loadCustomCadences(filename fileName: String) -> [Cadence] {
var cadences = [Cadence]()
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
cadences = try JSONDecoder().decode([Cadence].self, from: data)
return cadences
} catch {
print("error:\(error)")
}
}
return cadences
}
func saveCadences(_ cadence: Cadence) {
loadedCadences = loadCustomCadences(filename: "customCadences")
print("loa", loadedCadences)
do {
loadedCadences.append(cadence)
print("loazeaz", loadedCadences)
let data = try JSONEncoder().encode(loadedCadences)
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsDirectory.appendingPathComponent("customCadences.json")
try data.write(to: fileURL)
let jsonF = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
print(jsonF ?? "zerzer")
} catch {
print("Error saving JSON: \(error)")
}
}
The first print always returns an empty array despite loading from the same file I've just saved to, while the others respectively return the data I'm trying to save and its json entry, but nothing is ever appended when I'm saving the new data, it only loads and empty array and rewrites the file with only the latest data.
Any idea how to solve this ?
I want to write data to a custom JSON file, but it doesn't seem to load properly. The save function seems to work partially when I call it, but it looks like either it rewrites the file with only the newly saved data rather than the whole array of the json data + the new one, or there is a problem in my load function.
func loadCustomCadences(filename fileName: String) -> [Cadence] {
var cadences = [Cadence]()
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
cadences = try JSONDecoder().decode([Cadence].self, from: data)
return cadences
} catch {
print("error:\(error)")
}
}
return cadences
}
func saveCadences(_ cadence: Cadence) {
loadedCadences = loadCustomCadences(filename: "customCadences")
print("loa", loadedCadences)
do {
loadedCadences.append(cadence)
print("loazeaz", loadedCadences)
let data = try JSONEncoder().encode(loadedCadences)
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsDirectory.appendingPathComponent("customCadences.json")
try data.write(to: fileURL)
let jsonF = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
print(jsonF ?? "zerzer")
} catch {
print("Error saving JSON: \(error)")
}
}
The first print always returns an empty array despite loading from the same file I've just saved to, while the others respectively return the data I'm trying to save and its json entry, but nothing is ever appended when I'm saving the new data, it only loads and empty array and rewrites the file with only the latest data.
Any idea how to solve this ?
Share Improve this question asked Mar 13 at 2:42 JulienMJulienM 491 gold badge3 silver badges11 bronze badges 6 | Show 1 more comment2 Answers
Reset to default 1Your assumption in "The first print always returns an empty array despite loading from the same file I've just saved to, ..." is not correct. You are reading from a file in the
Bundle.main.url(forResource: ...)
, but you are writing to a different file, let fileURL = documentsDirectory.appendingPathComponent("customCadences.json")
.
To read and write to the same file, try this approach using the same file and directory you save the json data to:
func loadCustomCadences(filename fileName: String) -> [Cadence] {
var cadences = [Cadence]()
do {
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsDirectory.appendingPathComponent("customCadences.json")
let data = try Data(contentsOf: fileURL)
print("---> data: \(String(data: data, encoding: .utf8) as AnyObject)")
cadences = try JSONDecoder().decode([Cadence].self, from: data)
print("---> cadences: \(cadences)")
return cadences
} catch {
print("error:\(error)")
}
return cadences
}
func saveCadences(_ cadence: Cadence) {
var loadedCadences = loadCustomCadences(filename: "customCadences")
print("---> loa", loadedCadences)
do {
loadedCadences.append(cadence)
print("loazeaz", loadedCadences)
let data = try JSONEncoder().encode(loadedCadences)
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsDirectory.appendingPathComponent("customCadences.json")
try data.write(to: fileURL)
let jsonF = String(data: data, encoding: .utf8)
print("---> jsonF: \(jsonF)")
} catch {
print("Error saving JSON: \(error)")
}
}
The main reason is you're confusing between:
Bundle.main.url
: load the resources from your app bundle (your project folder)FileManager.default
: handle files and directories, even with iCloud.
And from your comments, I believe should use FileManager.default
only. I could refactor the code as below:
//Define the directory
private var cadencesDirectory: URL? {
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
.first?
.appendingPathComponent("customCadences")
.appendingPathExtension("json")
}
//Check whenever need to create the directory
private func checkValidDirectoryAndCreateIfNeeded() {
guard let cadencesDirectory else { return }
guard !FileManager.default.fileExists(atPath: cadencesDirectory.path) else { return }
do {
try FileManager.default.createDirectory(at: cadencesDirectory, withIntermediateDirectories: true)
} catch {
print("Cannot create the directory")
}
}
func load() -> [Cadence] {
checkValidDirectoryAndCreateIfNeeded()
if let path = cadencesDirectory?.path {
let data = FileManager.default.contents(atPath: path)
//TODO: JSONDecoder or whatever here
}
}
func save() {
...
checkValidDirectoryAndCreateIfNeeded()
if let cadencesDirectory {
...
data.write(to: cadencesDirectory)
}
}
. documentDirectory
, right? – sonle Commented Mar 13 at 3:19.documentDirectory
. Is it correct? – sonle Commented Mar 13 at 3:40loadCustomCadences
instead of usingif let url = Bundle.main.url(forResource: fileName, withExtension: "json")
use the same directory you use for saving the data, that is inside thedo
,let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let fileURL = documentsDirectory.appendingPathComponent("customCadences.json")
. Note noif let
. – workingdog support Ukraine Commented Mar 13 at 4:29