Reading JSON into array then writing it back to JSON Swift?

Viewed 50

New to IOS development and JSON stuff. I have a struct for Recipe which includes things like name, ingredients, instructions, etc. I have an array of Recipes. When my app is first run, I read data from a JSON file into the array of recipes so the app isn't empty at first. Throughout the app I append to the array of recipes. How would I go about writing the array back to the file everytime the array is changed? Here is some of the code and things I have tried.

Recipe Struct:

struct Recipe : Codable, Identifiable {
    var id: String
    var name: String
    var ingredients: [String]
    var instructions: [String]
    var category: String
    var imageName: String
}

Reading from JSON into recipe array:

import UIKit
import SwiftUI


var recipeData: [Recipe] = loadJson("recipeData.json")

func loadJson<T: Decodable>(_ filename: String) -> T {
    let data: Data
    guard let file = Bundle.main.url(forResource: filename,withExtension: nil)
    else {
        fatalError("\(filename) not found.")
    }
    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Could not load \(filename): \(error)")
    }
    do {
        return try JSONDecoder().decode(T.self, from: data)
    } catch {
        fatalError("Unable to parse \(filename): \(error)")
    }
}

My attempt to write back to a json file once array is changed(appended to):

func writeJson(){
        var jsonArray = [String]()
        
        if let documentDirectory = FileManager.default.urls(for: .documentDirectory,
                                                            in: .userDomainMask).first {
            let pathWithFilename = documentDirectory.appendingPathComponent("test.json")
        
            for recipe in recipeData{
                do{
                    let encodedData = try JSONEncoder().encode(recipe)
                    let jsonString = String(data: encodedData, encoding: .utf8)
                    print(jsonString!)
                    jsonArray.append(jsonString!)
                    try jsonString!.write(to: pathWithFilename,
                                         atomically: true,
                                         encoding: .utf8)
                }catch{
                    print(error)
                }
            }
        }
    }

This all builds successfully but nothing is written to my tests.json file. I am very new so any help would be appreciated. Thank you.

1 Answers
 try jsonString!.write(to: pathWithFilename,
                       atomically: true,
                       encoding: .utf8)

This method erases the existing file and replaces it with the data in question. You do this in a loop, so you're going to overwrite the file many times, always with a single recipe. My expectation is that this would always leave just the last recipe in the file.

I believe what you meant to do is:

// Encode all the recipes, not one at a time.
let encodedData = try JSONEncoder().encode(recipeData)

// Write them. There's no reason to convert to a string
encodedData.write(to: pathWithFilename, options: [.atomic])

As a beginner, this is probably fine. A more professional approach would likely spread this data over multiple files, use a database, or Core Data. But for small projects with only a few data items, writing a single JSON file is fine.

Related