Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Export Array using fileExporter

I am loading in a json file and creating an array. When a button is clicked, additional data is inserted into the array. What I want to do is export the modified array to a file. So essentially the array that has new data inserted into it.

What I'm not sure about is whether it is possible when exporting data from an array? or maybe I am going about this the wrong way?

EDIT: I don't necessarily want to export a json file, that was just the file type I first tried. I would be happy to export text files or csv's

ContentView

import SwiftUI
import UniformTypeIdentifiers


struct ContentView: View {
    @State private var name = ""
    @FocusState private var nameIsFocused: Bool
    @State var labels: [LabelData] = []
    @State var index = 0
    @State var saveFile = false


    var body: some View {
        HStack {
            Button(action: {
                index += 1
                
                    if index <= labels.count {
                        labels[index - 1]._label = "Yellow" }
                }) {
                    Text("Y")
                }
            Button(action: {
                saveFile.toggle()
                //print(labels[index - 1])
                }) {
                    Text("Export")
                        .frame(width: 100, height: 100)
                        .foregroundColor(Color(red: 0.362, green: 0.564, blue: 1))
                        .background(Color(red: 0.849, green: 0.849, blue: 0.849))
                        .clipShape(RoundedRectangle(cornerRadius: 25.0, style: .continuous))
                }
            .offset(x: 0, y: 0)
            .fileExporter(isPresented: $saveFile, document: Doc(url: Bundle.main.path(forResource: "labeldata", ofType: "json")!), contentType: .json) { (res) in
                
                do {
                    let fileUrl = try res.get()
                    print(fileUrl)
                }
                catch {
                    print("cannot save doc")
                    print(error.localizedDescription)
                }
            }
        }
        VStack{
                VStack {
                    if index < labels.count{
                        if let test = labels[index] {
                    Text(test._name)
                        }}}
                .offset(x: 0, y: -250)
                .frame(
                    minWidth: 0,
                    maxWidth: 325
                )
                VStack {
                    if index < labels.count{
                        if let test = labels[index] {
                    Text(test._name)
                        }}}
                .offset(x: 0, y: -150)
                .frame(
                    minWidth: 0,
                    maxWidth: 325
                )
                VStack {
                    if index < labels.count{
                        if let test = labels[index] {
                    Text(test._label)
                        }}}
                .offset(x: 0, y: -50)
                .frame(
                    minWidth: 0,
                    maxWidth: 325
                )
        }
        .onAppear {
            labels = load("labeldata.json")
        }
    }
}

struct Doc : FileDocument {
    var url : String
    static var readableContentTypes: [UTType]{[.json]}
    init(url : String) {
        self.url = url
    }
    init(configuration: ReadConfiguration) throws {
        url = ""
    }
    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        let file = try! FileWrapper(url: URL(fileURLWithPath: url), options: .immediate)
        return file
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
        }
}

LabelData

import Foundation

struct LabelData: Codable {

    var _id: Int
    var _name: String
    var _type: String
    var _description: String
    var _label: String
}

labeldata.json

[
    {
        "_id" : 1,
        "_name" : "Label1",
        "_type" : "type1",
        "_description" : "description1",
        "_label" : ""
    },
    {
        "_id" : 2,
        "_name" : "Label2",
        "_type" : "type2",
        "_description" : "description2",
        "_label" : ""
    }
]

DataLoader

import Foundation

func load<T: Decodable>(_ filename: String) -> T {
    let data: Data
    guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
        else {
            fatalError("Couldn't find \(filename) in main bundle.")
    }
    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
    }
    do {
        let decoder = JSONDecoder()
        return try decoder.decode(T.self, from: data)
    } catch {
        fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
    }
}
like image 364
neuron Avatar asked Mar 29 '26 17:03

neuron


1 Answers

The fileExporter writes in-memory data to location selected by a user, so we need to create document with our content and generate FileWrapper from content to exported data (CSV in this example).

So main parts, at first, exporter:

.fileExporter(isPresented: $saveFile, 
                 document: Doc(content: labels), // << document from content !!
              contentType: .plainText) {

and at second, document:

struct Doc: FileDocument {

    static var readableContentTypes: [UTType] { [.plainText] }

    private var content: [LabelData]
    init(content: [LabelData]) {
        self.content = content
    }

    // ...

    // simple wrapper, w/o WriteConfiguration multi types or
    // existing file selected handling (it is up to you)
    func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
        let text = content.reduce("") {
            $0 + "\($1._id),\($1._name),\($1._type),\($1._description),\($1._label)\n"
        }
        return FileWrapper(regularFileWithContents:
                 text.data(using: .utf8) ?? Data()) // << here !! 
    }

Tested with Xcode 13.4 / iOS 15.5

Test module is here

like image 101
Asperi Avatar answered Mar 31 '26 07:03

Asperi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!