1

I want to export all data of my entity "Log". I found this Code Example: https://gist.github.com/kenechilearnscode/2d5d35f550f593332319 But this won't work for me. It don´t add the Data of Core Data, the only output I get is: "This is what the app will export: date, label, typ" My Core Data Entity is "Log" with the attributes: date(type:date), labe1(type:String) and typ(type:Double). How can I export the Core Data to an CSV-File and send it via Mail? Thanks for any help :)

var logs : [Log] = []

func createExportString() -> String {


    var date: NSDate? = NSDate()
    var labe1: String?
    var typ: Double

    var export: String = NSLocalizedString("date, label, typ, \n", comment: "")


    for (index, log) in logs.enumerated() {
        if index < logs.count - 1 {

            date = Date() as? NSDate
            label =  log.value(forKey: "time") as? String
            typ = (log.value(forKey: "type") as? Double)!


            let dateString = "\(log.date!)"
            let labelString = "\(log.labe1!)"
            let typeString = "\(log.typ)"

            export += dateString + "," + labelString + "," + typeString + "," + "\n"
        }
    }
    print("This is what the app will export: \(export)")
    return export
}

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


    func saveAndExport(exportString: String) {
        let exportFilePath = NSTemporaryDirectory() + "export.csv"
        let exportFileURL = NSURL(fileURLWithPath: exportFilePath)
        FileManager.default.createFile(atPath: exportFilePath, contents: NSData() as Data, attributes: nil)
        var fileHandleError: NSError? = nil
        var fileHandle: FileHandle? = nil
        do {
            fileHandle = try FileHandle(forWritingTo: exportFileURL as URL)
        } catch {
            print("Error with fileHandle")
        }

        if fileHandle != nil {
            fileHandle!.seekToEndOfFile()
            let csvData = exportString.data(using: String.Encoding.utf8, allowLossyConversion: false)
            fileHandle!.write(csvData!)

            fileHandle!.closeFile()

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

            activityViewController.excludedActivityTypes = [
                UIActivityType.assignToContact,
                UIActivityType.saveToCameraRoll,
                UIActivityType.postToFlickr,
                UIActivityType.postToVimeo,
                UIActivityType.postToTencentWeibo
            ]

            self.present(activityViewController, animated: true, completion: nil)
        }
    }

EDIT:

I try to add these:

let context = DatabaseController.persistentContainer.viewContext
    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Log")
    let result = try! NSManagedObjectContext.execute(fetchRequest)


    logs = [result]

But I get the error: "Use of instance member 'execute' on type 'NSManagedObjectContext'; did you mean to use a value of type 'NSManagedObjectContext' instead?"

EDIT 2:

With these:

    do {
        let results = try context.execute(fetchRequest)
    }
    catch {
        print(error)
    }

I get the error on the line where "logs = [result]: Use of unresolved identifier 'result'

ma09111
  • 33
  • 1
  • 6
  • Where's your attempt to iterate over the data from core data? The code you posted simply iterates over an empty array (`logs`). – rmaddy Oct 24 '16 at 15:53
  • Thanks for your answer, I have edit the original post. – ma09111 Oct 24 '16 at 16:14
  • I don't much about core data but shouldn't you be calling `execute` on your `context` variable? `execute` is an instance method, not a type method. – rmaddy Oct 24 '16 at 16:18
  • Thanks for your help, I have edit the original post, but I get a new error. – ma09111 Oct 24 '16 at 16:31
  • You need the `logs = [results]` inside the `do` block. – rmaddy Oct 24 '16 at 16:33
  • This also don't work :( Error: Cannot convert value of type 'NSPersistentStoreResult' to expected element type 'Log' ... Sorry I am very new at this all... – ma09111 Oct 24 '16 at 16:41

1 Answers1

6

This my solution that I use for Swift 4.2.

UPDATED to match code in repository.

import UIKit
import CoreData

class ViewController: UIViewController {

    var itemid = 178
    var nametext = "Jones3"
    var amountDouble = 68
    var inventoryDate: Date? = Date()
    var stockStatus = true
    var fetchedStatsArray: [NSManagedObject] = []
    let context = CoreDataStack.context

    override func viewDidLoad() {
        super.viewDidLoad()
        // This add a new record every time the app is run
        storeTranscription()
        // Loads the current data
        getTranscriptions()
    }

    @IBAction func exportButton(_ sender: UIButton) {
        exportDatabase()
    }

    func storeTranscription() {
        //retrieve the entity that we just created
        let entity =  NSEntityDescription.entity(forEntityName: "ItemList", in: context)
        let transc = NSManagedObject(entity: entity!, insertInto: context) as! ItemList

        //set the entity values
        transc.itemID = Double(itemid)
        transc.productname = nametext
        transc.amount = Double(amountDouble)
        transc.stock = stockStatus
        transc.inventoryDate = inventoryDate

        //save the object
        do {
            try context.save()
            print("saved!")
        } catch let error as NSError  {
            print("Could not save \(error), \(error.userInfo)")
        } catch {

        }
    }

    func getTranscriptions () {
        //create a fetch request, telling it about the entity
        let fetchRequest: NSFetchRequest<ItemList> = ItemList.fetchRequest()

        do {
            //go get the results
            let searchResults = try context.fetch(fetchRequest)
            fetchedStatsArray = searchResults as [NSManagedObject]
            //I like to check the size of the returned results!
            print ("num of results = \(searchResults.count)")
            //You need to convert to NSManagedObject to use 'for' loops
            for trans in searchResults as [NSManagedObject] {
                //get the Key Value pairs (although there may be a better way to do that...
                print("\(trans.value(forKey: "productname")!)")
                let mdate = trans.value(forKey: "inventoryDate") as! Date
                print(mdate)
            }

        } catch {
            print("Error with request: \(error)")
        }
    }

    func exportDatabase() {
        let exportString = createExportString()
        saveAndExport(exportString: exportString)
    }

    func saveAndExport(exportString: String) {
        let exportFilePath = NSTemporaryDirectory() + "itemlist.csv"
        let exportFileURL = NSURL(fileURLWithPath: exportFilePath)
        FileManager.default.createFile(atPath: exportFilePath, contents: NSData() as Data, attributes: nil)
        //var fileHandleError: NSError? = nil
        var fileHandle: FileHandle? = nil
        do {
            fileHandle = try FileHandle(forWritingTo: exportFileURL as URL)
        } catch {
            print("Error with fileHandle")
        }

        if fileHandle != nil {
            fileHandle!.seekToEndOfFile()
            let csvData = exportString.data(using: String.Encoding.utf8, allowLossyConversion: false)
            fileHandle!.write(csvData!)

            fileHandle!.closeFile()

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

            activityViewController.excludedActivityTypes = [
                UIActivity.ActivityType.assignToContact,
                UIActivity.ActivityType.saveToCameraRoll,
                UIActivity.ActivityType.postToFlickr,
                UIActivity.ActivityType.postToVimeo,
                UIActivity.ActivityType.postToTencentWeibo
            ]

            self.present(activityViewController, animated: true, completion: nil)
        }
    }

    func createExportString() -> String {
        var itemIDvar: NSNumber?
        var productNamevar: String?
        var amountvar: NSNumber?
        var stockvar: Bool?

        var export: String = NSLocalizedString("itemID, productName, Amount \n", comment: "")
        for (index, itemList) in fetchedStatsArray.enumerated() {
            if index <= fetchedStatsArray.count - 1 {
                itemIDvar = itemList.value(forKey: "itemID") as! NSNumber?
                productNamevar = itemList.value(forKey: "productname") as! String?
                amountvar = itemList.value(forKey: "amount") as! NSNumber?
                stockvar = itemList.value(forKey: "stock") as! Bool?
                let inventoryDatevar = itemList.value(forKey: "inventoryDate") as! Date
                let itemIDString = itemIDvar
                let procductNameSting = productNamevar
                let amountSting = amountvar
                let stockSting = stockvar
                let inventoryDateSting = "\(inventoryDatevar)"
                export += "\(itemIDString!),\(procductNameSting!),\(stockSting!),\(amountSting!),\(inventoryDateSting) \n"
            }
        }
        print("This is what the app will export: \(export)")
        return export
    }   
}

Project Files

MwcsMac
  • 6,810
  • 5
  • 32
  • 52
  • Why are you using a mix of Swift arrays and `NSArray`? Just stick with Swift collections. – rmaddy Oct 24 '16 at 16:44
  • How does look your fetchedStatsArray? – ma09111 Oct 24 '16 at 16:49
  • This don't work for me, error: 'AppDelegate' has no member 'managedObjectContext' "let context: NSManagedObjectContext = appDel.managedObjectContext" – ma09111 Oct 24 '16 at 17:27
  • @ma09111 See updated code. The last code I posted had a mix of old core data programing and I have included access to an example project. – MwcsMac Oct 24 '16 at 19:26
  • Thank you, looks good, but how can i add an date, double and boolean to the export string? – ma09111 Oct 24 '16 at 19:41
  • I got it, but only date won't work and there is always a new entry, can I hide this new Core Data entry? My data from Cora data are also shown in a UITableView. – ma09111 Oct 24 '16 at 20:23
  • @ma09111I have updated the code both in the answer and in the project files. The fact that your Core Data is displayed in UITableView will not affect exporting it to CSV. – MwcsMac Oct 26 '16 at 18:08
  • Hi, thanks for you post. Everytime the func storeTranscription() and func getTranscriptions () is running I get an extra entry in my table view see here: https://www.dropbox.com/s/fsk412lmze919nw/SimulatorScreenShot.png?dl=0 Must I delete the last entry in Cora Data while func exportDatabase() is running? – ma09111 Oct 27 '16 at 13:01
  • @ma09111 The `storeTranscription()` should only need to be called when you need to save data. Then only need to call `getTranscriptions ()` before you need to the export. – MwcsMac Oct 27 '16 at 16:59