Home > Software engineering >  SwiftUI update/replace key, value in a JSON file
SwiftUI update/replace key, value in a JSON file

Time:01-14

I am new in SwiftUI coding. I have a JSON file with below data structure. Trying to update the value of specific key with TextField variable while button pressed. I am able to read the JSON file content but not able to figure out how to update specific keys and write back to the file. This is for a test MacOS App. Please help me. Thanks in advance.

\\settingsData.json
[
    {
        "id" : 1001,
        "username": "example",
        "password": "123456",
    }
]
import SwiftUI
import Foundation

var jsonArray: [jsonModel] = readJSON("settingsData")

struct testJSON: View {
    @State private var UserName: String = ""
    
    var body: some View {
        VStack (alignment: .leading) {
          
            ForEach(jsonArray) { jsonValue in
                Text("Username is \(jsonValue.username)")
            }

            TextField("Username", text: $UserName)
            
            Button(action: {
                // Update array and save to file
                print("Done")
            })
            {
                    Text("Save")
            }
        }
    }
}

Here is the Model

struct jsonModel: Hashable, Codable, Identifiable {
    var id: Int
    var username: String
    var password: String
}

And this is the simple code that I am using to read JSON data from a file inside the App directory.

func readJSON<T: Decodable>(_ filename: String) -> T {
    let data: Data
    
    guard let file = Bundle.main.url(forResource: filename, withExtension: "json")
    else {
        fatalError("Couldn't find \(filename) in main bundle.")
    }
    
    do {
        print(file)
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
    }
    
    do {
        let decoder = JSONDecoder()
        let jsonData = try decoder.decode(T.self, from: data)
    
        return jsonData
    } catch {
        fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
    }
}

CodePudding user response:

Here is some code that shows you how to read and write from file on macOS (given the correct permissions).

With this you should be able to write code to "...update specific keys and write back to a file...".

As I mentioned in my comment, the app bundle is read-only. So you have to write the updates to the "documentDirectory" (for example), using the FileManager.

import Foundation
import SwiftUI

@main
struct MacApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
 
struct ContentView: View {
    @StateObject var dataManager = DataManager()
    @State private var userName: String = ""
    
    var body: some View {
        VStack (alignment: .leading) {
            
            ForEach(dataManager.jsonArray) { jsonValue in
                Text("Username is \(jsonValue.username)")
            }
            
            TextField("Username", text: $userName)
            
            Button(action: {
                // Update array, just for testing update all userName
                for i in dataManager.jsonArray.indices {
                    dataManager.jsonArray[i].username = userName
                }
                // save to file
                dataManager.saveToFile()
                print("Done")
            })
            {
                Text("Save")
            }
        }.frame(width: 444, height: 444)
    }
    
}

class DataManager: ObservableObject {
    @Published var jsonArray: [JsonModel] = []
    
    var fileName = "settingsData"
    
    init() {
        readFromFile()
    }
    
    func readFromFile() {
        jsonArray = readJSONBundle(fileName) // do this only the first time you open the app
      //  readJSON() // read from the saved file
    }
    
    private func readJSONBundle<T: Decodable>(_ filename: String) -> T {
        let data: Data
        
        guard let file = Bundle.main.url(forResource: filename, withExtension: "json")
        else {
            fatalError("Couldn't find \(filename) in main bundle.")
        }
        do {
            print(file)
            data = try Data(contentsOf: file)
        } catch {
            fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
        }
        do {
            let decoder = JSONDecoder()
            let jsonData = try decoder.decode(T.self, from: data)
            return jsonData
        } catch {
            fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
        }
    }

    func saveToFile() {
        do {
            let furl = try FileManager.default
                .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
                .appendingPathComponent(fileName)
                .appendingPathExtension("json")
            print("---> writing to: \(furl)")
            let data = try JSONEncoder().encode(jsonArray)
            try data.write(to: furl)
        } catch {
            print("---> error saveToFile: \(error)")
        }
    }
    
    func readJSON() {
        do {
            let furl = try FileManager.default
                .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
                .appendingPathComponent(fileName)
                .appendingPathExtension("json")
            print("---> reading from: \(furl)")
            let data = try Data(contentsOf: furl)
            let decoder = JSONDecoder()
            let jsonData = try decoder.decode([JsonModel].self, from: data)
            jsonArray = jsonData
        } catch {
            print("---> error reading from file: \(error)")
        }
    }
    
}

struct JsonModel: Hashable, Codable, Identifiable {
    var id: Int
    var username: String
    var password: String
}
  •  Tags:  
  • Related