Search code examples
iosswiftcsvcore-dataswiftui

SwiftUI - Exporting Core Data to CSV file - cannot iterate through entity


I am writing an app using SwiftUI. I am trying to export all entries of one Entity (NPTransactions, attributes: date (Date), type(String)) in Core Data into CSV file. I want to use Share Sheet, so users could for example save the file to their phones.

What I know how to do is: - create CSV file - save CSV file - open Share Sheet

The problem is: I don't know how to iterate through Entity entries and create new line of text in CSV file.

I wanted to create an array from my entity like this:

var transactions = [NPTransaction]()

Later on I was hoping to iterate through this array and add new line of text for every entry. But it doesn't work. No text is being added from this for loop.

My code is like this:

import SwiftUI

struct SettingsView: View {

    @State private var isShareSheetShowing = false

    var transactions = [NPTransaction]()

    var body: some View {
        NavigationView {
            Button(action: shareButton)
            {
                HStack(alignment: .firstTextBaseline) {
                    Text("Export CSV")
                        .font(.headline)
                    Image(systemName: "square.and.arrow.up")
                        .font(.title)
                }
            }
        }
        .navigationBarTitle("Settings")
    }

    func shareButton() {
        let fileName = "export.csv"
        let path = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
        var csvText = "Date,Type\n"

        // This test works
        //      for _ in 1...5 {
        //          csvText += "test 1, test 2\n"
        //      }

        // This doesn't work
        for transaction in transactions {
            csvText += "\(transaction.date ?? Date()),\(transaction.type ?? "-")\n"
        }           

        do {
            try csvText.write(to: path!, atomically: true, encoding: String.Encoding.utf8)
        } catch {
            print("Failed to create file")
            print("\(error)")
        }
        print(path ?? "not found")

        var filesToShare = [Any]()
        filesToShare.append(path!)

        let av = UIActivityViewController(activityItems: filesToShare, applicationActivities: nil)

        UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true, completion: nil)

        isShareSheetShowing.toggle()
    }

}

struct SettingsView_Previews: PreviewProvider {
    static var previews: some View {
        SettingsView()
    }
}

Is the problem with my for loop or should I make it a different way? For example using fetchRequest somehow?


Solution

  • Eureka! :) I was missing a @FetchRequest:

    @FetchRequest(entity: NPTransaction.entity(), sortDescriptors: []) var transactions: FetchedResults<NPTransaction>
    

    Full code below. Maybe it will be useful for someone else.

    import SwiftUI
    
    struct SettingsView: View {
    
        @FetchRequest(entity: NPTransaction.entity(), sortDescriptors: []) var transactions: FetchedResults<NPTransaction>
        @State private var isShareSheetShowing = false
    
        var body: some View {
            NavigationView {
                Button(action: shareButton)
                {
                    HStack(alignment: .firstTextBaseline) {
                        Text("Export CSV")
                            .font(.headline)
                        Image(systemName: "square.and.arrow.up")
                            .font(.title)
                    }
                }
            }
            .navigationBarTitle("Settings")
        }
    
        func shareButton() {
            let fileName = "export.csv"
            let path = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
            var csvText = "Date,Type\n"
    
            for transaction in transactions {
                csvText += "\(transaction.date ?? Date()),\(transaction.type ?? "-")\n"
            }
    
            do {
                try csvText.write(to: path!, atomically: true, encoding: String.Encoding.utf8)
            } catch {
                print("Failed to create file")
                print("\(error)")
            }
            print(path ?? "not found")
    
            var filesToShare = [Any]()
            filesToShare.append(path!)
    
            let av = UIActivityViewController(activityItems: filesToShare, applicationActivities: nil)
    
            UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true, completion: nil)
    
            isShareSheetShowing.toggle()
        }
    
    }
    
    struct SettingsView_Previews: PreviewProvider {
        static var previews: some View {
            SettingsView()
        }
    }