12

In an app I created to collect data from Apple pencil input, I tried to export the data into a CSV file. But so far, I only managed to create a single column which records the time length. I want to add another column to record the force from the Apple pencil.

This is what I have tried to do:

var patientsData:[Dictionary<String, AnyObject>] = Array()
var dct = Dictionary<String, AnyObject>()

// MARK: CSV writing
func createCSVX(from recArray:[Dictionary<String, AnyObject>]) {
var csvString = "\("Time")\n"
dct.updateValue(TestDraw.time as AnyObject, forKey: "T")
csvString = csvString.appending("\(String(describing: dct["T"]))\n")
patientsData.append(dct)
let fileManager = FileManager.default
do {
    let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
    let fileURL = path.appendingPathComponent("TrailTime.csv")
    try csvString.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
    print("error creating file")
    }

}

I know I can write another function to create another CSV file with a single column to record the force, but I would like to record them in a single spreadsheet​.

Also, does anyone know how to remove the "Optional" in the CSV file created?

This is what I have tried based on one of the answers.

func createCSVX(from recArray:[Dictionary<String, AnyObject>]) {
var csvString = "\("Time"),\("Force")\n"

dct.updateValue(TestDraw.time as AnyObject, forKey: "T")

dct.updateValue(TestDraw.force as AnyObject, forKey: "F")

patientsData.append(dct)

csvString = csvString.appending("\(String(describing: dct["T"])), \(String(describing:   dct["F"]))\n")

let fileManager = FileManager.default

do {

let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil , create: false )

let fileURL = path.appendingPathComponent("TrailTime.csv")

try csvString.write(to: fileURL, atomically: true , encoding: .utf8)
} catch {

print("error creating file")

}

print(TestDraw.force)

}

Screeshot of the exported CSV file

Fëanor Tang
  • 151
  • 1
  • 2
  • 8
  • 1
    using this library CHCSVParser it's really easy to create a csv – Alastar Apr 26 '19 at 14:57
  • Is it already in the swift library that can be directly imported? – Fëanor Tang Apr 26 '19 at 15:00
  • nope you should add it to to your podfile in this way: pod 'CHCSVParser' – Alastar Apr 26 '19 at 15:01
  • Wow, thanks! I will do some research on how to use CocoaPods later. I am new in Swift and I didn't know CocoaPods before. – Fëanor Tang Apr 26 '19 at 15:05
  • use this guide, it's pretty simple: https://www.raywenderlich.com/626-cocoapods-tutorial-for-swift-getting-started – Alastar Apr 26 '19 at 15:07
  • It's not even necessary to use CocoaPods. I managed to successfully run CHCSVParser in a vanilla SwiftUI project by following this amazing tutorial: https://youtu.be/7luhStOgXjk You just have to copy two files from the repo into your project's directory. – Dan Sep 26 '22 at 23:05

2 Answers2

19

Tutorial copied from https://iostutorialjunction.com/2018/01/create-csv-file-in-swift-programmatically.html:

Step 1:

Create an array, named as "employeeArray" which will store all our records for the employees as key value objects. Also we will add dummy data to the newly created array

class ViewController: UIViewController {
 var employeeArray:[Dictionary<String, AnyObject>] =  Array()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
   for i in 1...10 {
            var dct = Dictionary<String, AnyObject>()
            dct.updateValue(i as AnyObject, forKey: "EmpID")
            dct.updateValue("NameForEmplyee id = \(i)" as AnyObject, forKey: "EmpName")
            employeeArray.append(dct)
        }
    }
}

Step 2: Now we have data with us, and its time to create CSV(comma separated values) file using swift programmatically. For this we will loop through our records in "employeeArray" and append them in a string. Then we will write this string to our document directory of the app. All the stuff goes in different function named as "createCSV", below is the code for the same

func createCSV(from recArray:[Dictionary<String, AnyObject>]) {
        var csvString = "\("Employee ID"),\("Employee Name")\n\n"
        for dct in recArray {
            csvString = csvString.appending("\(String(describing: dct["EmpID"]!)) ,\(String(describing: dct["EmpName"]!))\n")
        }
        
        let fileManager = FileManager.default
        do {
            let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
            let fileURL = path.appendingPathComponent("CSVRec.csv")
            try csvString.write(to: fileURL, atomically: true, encoding: .utf8)
        } catch {
            print("error creating file")
        }

    }

Step 3: Finally we will call our function from "viewDidLoad". Below is the complete code

class ViewController: UIViewController {
 var employeeArray:[Dictionary<String, AnyObject>] =  Array()
    
    override func viewDidLoad() {
        super.viewDidLoad()
         for i in 1...10 {
                   var dct = Dictionary<String, AnyObject>()
                   dct.updateValue(i as AnyObject, forKey: "EmpID")
                   dct.updateValue("NameForEmplyee id = \(i)" as AnyObject, forKey: "EmpName")
                   employeeArray.append(dct)
               }

               createCSV(from: employeeArray)

    }
 
 func createCSV(from recArray:[Dictionary<String, AnyObject>]) {
        var csvString = "\("Employee ID"),\("Employee Name")\n\n"
        for dct in recArray {
            csvString = csvString.appending("\(String(describing: dct["EmpID"]!)) ,\(String(describing: dct["EmpName"]!))\n")
        }
        
        let fileManager = FileManager.default
        do {
            let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
            let fileURL = path.appendingPathComponent("CSVRec.csv")
            try csvString.write(to: fileURL, atomically: true, encoding: .utf8)
        } catch {
            print("error creating file")
        }

    }
 }
Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
Rahmouni Rabii
  • 934
  • 1
  • 7
  • 16
  • I have tried what you suggested, but unfortunately, the data in Force all go​ into the Time column. Would you please take a look at the change I made in the original question and point out what goes wrong? – Fëanor Tang Apr 28 '19 at 00:07
  • you forgot the ! at the end to remove optional csvString = csvString.appending("\(String(describing: dct["T"]!)), \(String(describing: dct["F"]!))\n") – Rahmouni Rabii Apr 28 '19 at 10:07
  • Oh right, thanks! But why all the 0 is under Time, instead of Force, where it is supposed to be – Fëanor Tang Apr 28 '19 at 14:06
  • 1
    from above csv is comma separated file. But if any string is itself contains comma then this fails. Please suggest any way to overcome that – Protocol Sep 21 '20 at 16:05
  • 1
    where could we find the file? I can't see it in Files app – user4150758 Nov 24 '21 at 06:13
2

Excellent answer above, made a slight modification to address specific instances in the data. You can modify individual components as needed and remove commas, trim UUIDs, etc. Note this solution uses transactions stored in a list of Core Data objects. I also print the location of the data file so you can check it in the simulator.

  func createCSVFile() {
        var csvString = "id,name,description,category,date,type,receipt,amount\n"
        for trans in transactions {
            let transID = trans.id!.debugDescription.split(separator: "-")[0].replacingOccurrences(of: ")", with: "")
            let transName = trans.name!
            let transDesc = trans.desc!.replacingOccurrences(of: ",", with: "-")
            let transCat = trans.category!
            let transDate = trans.date!
            let transType = trans.type!
            
            var transReceipt = "None"
            if trans.receipt == nil {
                transReceipt = "Present"
            }
            let transAmount = trans.amount
            let dataString = "\(transID),\(transName),\(transDesc),\(transCat),\(transDate),\(transType),\(transReceipt),\(transAmount)\n"
            print("DATA: \(dataString)")
            csvString = csvString.appending(dataString)
        }

        let fileManager = FileManager.default
        do {
            let path = try fileManager.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil, create: false)
            print("PATH: \(path)")
            let fileURL = path.appendingPathComponent("CSVData.csv")
            try csvString.write(to: fileURL, atomically: true, encoding: .utf8)
        } catch {
            print("error creating file")
        }
    }
RandallShanePhD
  • 5,406
  • 2
  • 20
  • 30