6

I'm building an app with core data (1 entity with 5 attributes) that display in a tableView. Now i would like to export this data to a CSV file (so i can send this file with mail from phone) so i can open it in excel on a windows. i search a lot but didn't find the right answer. Can someone help me or give me a link to a good explanation or tutorial?

I'm building in swift btw.

func createExportString() -> String {
    var merk: String?
    var ref: String?
    var beschrijving: String?
    var aantal: String?
    var wbs: String?

    var export = NSLocalizedString("merk, ref, beschrijving, aantal, wbs \n", comment: "")
            merk = Lijst.valueForKey("merk") as? String
            ref = Lijst.valueForKey("ref") as? String
            aantal = Lijst.valueForKey("aantal") as? String
            beschrijving = Lijst.valueForKey("beschrijving") as? String
            wbs = Lijst.valueForKey("wbs") as? String


            let merkString = "\(merk!)" ?? "-"
            let refString = "\(ref!)" ?? "-"
            let beschString = "\(beschrijving!)" ?? "-"
            let aantalString = "\(aantal!)" ?? "-"
            let wbsString = "\(wbs!)" ?? "-"

            export += merkString + "," + refString + "," + beschString + "," + aantalString +
                "," + wbsString + "\n"

    print("This is what the app will export: \(export)")
    return export
}

@IBAction func saveToCSV(sender: AnyObject) {
    exportDatabase()
}

func exportDatabase() {
    var exportString = createExportString()
    saveAndExport(exportString)
}

func saveAndExport(exportString: String) {
    let exportFilePath = NSTemporaryDirectory() + "export.csv"
    let exportFileURL = NSURL(fileURLWithPath: exportFilePath)
    NSFileManager.defaultManager().createFileAtPath(exportFilePath, contents: NSData(), attributes: nil)
    var fileHandleError: NSError? = nil
    var fileHandle: NSFileHandle? = nil
    do {
        fileHandle = try NSFileHandle(forWritingToURL: exportFileURL)
    } catch {
        print( "Error with fileHandle")
    }

    if fileHandle != nil {
        fileHandle!.seekToEndOfFile()
        let csvData = exportString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
        fileHandle!.writeData(csvData!)

        fileHandle!.closeFile()

        let firstActivityItem = NSURL(fileURLWithPath: exportFilePath)
        let activityViewController: UIActivityViewController = UIActivityViewController(activityItems: [firstActivityItem], applicationActivities: nil)

        self.presentViewController(activityViewController, animated: true, completion: nil)
    }
}
adams.s
  • 185
  • 1
  • 14
  • Check http://stackoverflow.com/questions/10572172/how-to-export-core-data-to-csv – Ulysses Feb 20 '16 at 23:48
  • @UlyssesR i already came to that one, but i'm just a beginner and don't really have an idea on how to convert obejctive-c to swift... – adams.s Feb 21 '16 at 00:03
  • Maybe this step by step video help you, https://www.youtube.com/watch?v=iBZbMkmtHuU – Ulysses Feb 21 '16 at 00:05
  • @UlyssesR already did that video to :p, i've imported that with my data into my app but i'm getting error's on the createExportString function. – adams.s Feb 21 '16 at 00:07
  • Can you put the code and the error? may help us to debug the code. – Ulysses Feb 21 '16 at 00:09
  • @UlyssesR i added the code, the error comes when merk = lijst.valueForKey... and the error goes to my appDelegate with thread 1: signal SIGABRT but all the connections with the main.storyboard are fine edit: lijst is the name of the entity from core data – adams.s Feb 21 '16 at 00:25

1 Answers1

9

This is a concise way of doing all you want - you pass array of managed objects and a string that is CSV's filename. The method writes your CSV to a file. I believe you already have most of this in your code, the thing that is lacking are last two lines that just write a string into a new file in "Documents" directory.

func writeCoreDataObjectToCSV(objects: [NSManagedObject], named: String) -> String? {
    /* We assume that all objects are of the same type */
    guard objects.count > 0 else {
        return nil
    }
    let firstObject = objects[0]
    let attribs = Array<Any>(firstObject.entity.attributesByName.keys)
    let csvHeaderString = (attribs.reduce("",combine: {($0 as String) + "," + $1 }) as NSString).substringFromIndex(1) + "\n"

    let csvArray = objects.map({object in
        (attribs.map({(object.valueForKey($0) ?? "NIL").description}).reduce("",combine: {$0 + "," + $1}) as NSString).substringFromIndex(1) + "\n"
    })
    let csvString = csvArray.reduce("", combine: +)

    return csvHeaderString+csvString
}

Now, somewhere else in the code you need to create NSData from this string and add it to mail composer:

let csvString = .....
let data = csvString.dataUsingEncoding(NSUTF8StringEncoding)
let composer = MFMailComposeViewController()
composer.addAttachmentData(attachment: data,
          mimeType mimeType: "text/csv",
          fileName filename: "mydata.csv")

Then, do the usual stuff with the composer (set body, topic, etc) and present it to the user!

EDIT:

Edited the answer to better answer the question.

Terminus
  • 925
  • 10
  • 23
  • can you maybe give me some more explanation for this code? where do you put it in a file and where do you put this code in the viewcontroller? – adams.s Feb 21 '16 at 00:29
  • Allright, my mistake. This does basically what let export = does. I'll edit the answer – Terminus Feb 21 '16 at 00:33
  • ok. did u already edited your answer? i don't really see a difference? :p – adams.s Feb 21 '16 at 00:46
  • ok i think i understand it? but where do i find the csv file then so i can use it? – adams.s Feb 21 '16 at 01:05
  • DocumentsDir variable holds the exact path to your file. Maybe the name is a bit unfortunate :) You can return it from the function and then use it somewhere else (e.g. mail controller). – Terminus Feb 21 '16 at 09:17
  • Ok i added your code for testing and to see how it works, but i don't really understand how i can return the file if i don't know what the file is? and i added a MailController wich works but how do i get the csv file then in the mail.setMessageBody("here has to come the csv-file???" , isHTML: false) – adams.s Feb 21 '16 at 13:05
  • You should add it as an attachment - see https://developer.apple.com/library/prerelease/ios/documentation/MessageUI/Reference/MFMailComposeViewController_class/index.html#//apple_ref/occ/instm/MFMailComposeViewController/addAttachmentData:mimeType:fileName: – Terminus Feb 21 '16 at 18:07
  • ok thx i'm getting their almost.. :D when i click my save button it brings me to the mail with the csv file attached. when i open it on the computer everything seems in it. but everything is in the wrong order,.. so i remember i have to put it with comma's? so i have 5 attributes like "attribute1, at2, at3, at4, at5," but that doesn't seem to work? – adams.s Feb 21 '16 at 18:46
  • i think i found it! i let you know in a few minutes :D – adams.s Feb 21 '16 at 18:53