BLACK FRIDAY: Save 50% on all books and bundles! >>

SOLVED: Problem adding another entry to JSON file

Forums > Swift

Hello,

For some reason, I am unable to add a second item to my custom JSON file. I'm fairly sure I'm missing something elementary, but couldn't figure it out yet.

struct Item: Codable {
    var name: String
}

func writeJSON(_ entry: Item) {
    var array = [Item]()
    array.append(entry)

    do {
        let fileURL = try FileManager.default.url(for: .desktopDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent("example.json")

        let encoder = JSONEncoder()
        try encoder.encode(array).write(to: fileURL)
    } catch {
        print(error.localizedDescription)
    }
}

writeJSON(Item(name: "hello"))

Above code writes JSON fine, but when I run the function again with a different entry, the only entry gets replaced. Any ideas?

   

You create an empty Item array each time the function is called. Then you append the one item and only that item to the array. So the array you write to disk will only ever have one item. Depending on your use case you can do several things, but all involve storing the item array with all that items somewhere.

Instead of declaring it inside your function, declare it at the top of the file and inside the function append the new item. Then write the array to disk, that will now contain all items!

   

Thank you for your reply. You're totally right about the array initialization, however, if I declare it outside the function, I still can't get it to work.

Below latest playground code:

struct Item: Codable {
    var name: String

    enum CodingKeys: String, CodingKey {
        case name
    }
}

var array = [Item]()

func writeJSON(_ entry: Item) {
    array.append(entry)

    do {
        let fileURL = try FileManager.default.url(for: .desktopDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent("deneme.json")

        let encoder = JSONEncoder()
        try encoder.encode(entry).write(to: fileURL)
    } catch {
        print(error.localizedDescription)
    }
}

writeJSON(Item(name: "hello"))

I tried encoding both entry and the array itself separately, but all I see is just one entry.

   

hi,

at some point, you need to read the items back into the program (you only write data in this program). and i think you want to write all of the Items in the array, not just a single Item. (i.e., your writeJSON function appends an item to the array, but then writes out only the one item appended (not the entire array).

this code should work for you: it declares the array of Items with a code block that will be executed the first time you code references the array variable. that code block will load what's out there on disk already.

struct Item: Codable {
    var name: String

    enum CodingKeys: String, CodingKey {
        case name
    }
}

var array: [Item] = {
    do {
        let fileURL = try FileManager.default.url(for: .desktopDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent("deneme.json")

        let data = try Data(contentsOf: fileURL)
        let decoder = JSONDecoder()
        let items = try decoder.decode([Item].self, from: data)
        return items
    } catch {
        print(error.localizedDescription)
        return []
    }
}()

i'd rewrite the writeJSON function to write out the whole array:

func writeJSON(items: [Item]) {
    do {
        let fileURL = try FileManager.default.url(for: .desktopDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent("deneme.json")

        let encoder = JSONEncoder()
        try encoder.encode(array).write(to: fileURL)
    } catch {
        print(error.localizedDescription)
    }
}

from there, here's some basic code that shows you what's happening

// show what's out there already on disk
print(array.map({ $0.name }))

// append a few items and then write the array
array.append(Item(name: "hello"))
array.append(Item(name: "goodbye"))
array.append(Item(name: "C U L8R"))
writeJSON(items: array)

// append a few more items, and print the names

array.append(Item(name: "here is"))
array.append(Item(name: "more data"))
// show what's in the array
print(array.map({ $0.name }))

// save these last two as well.
writeJSON(items: array)

the first time you run this code (and there is no existing file on your desktop), your output will look like this:

The file “deneme.json” couldn’t be opened because there is no such file.

[]

["hello", "goodbye", "C U L8R", "here is", "more data"]

the second time you run, all the items from the first run will be there at the start, and you'll then add even more items, producing this output:

["hello", "goodbye", "C U L8R", "here is", "more data"]

["hello", "goodbye", "C U L8R", "here is", "more data", "hello", "goodbye", "C U L8R", "here is", "more data"]

hope that helps, DMG

2      

Thank you! This worked like a charm, and I understand the logic now.

   

@bitigchi i used your function to write my json but when i close my app and open it again the code reverts to my original json not the new appended version

   

Save 50% in my Black Friday sale.

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

Not logged in

Log in
 

Link copied to your pasteboard.