Create CSV File in Swift and Write to File

How to create a CSV file using Swift

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")
}

}
}

Create CSV file in Swift and write to file

convertMutable can be easily written to disk with either fun writeToFile(_ path: String, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws or func writeToURL(_ url: NSURL, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws. All you have to do is create a path or URL to write the string out to. If you are using iCloud things will be more challenging but for locally stored files you can use let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString to get the root path of the documents directory.

Update: Based on you first comment below here is some added info:
The first issue is that it appears as though you are looking for code you can just paste int your project without really understanding what it does. My appologies if I'm wrong, but if I'm right this is not a good route to take as you will have many issues down the road when things change.

At the bottom of your last code section you are trying to create a function inside a function which is not going to do what you want. The above mentioned functions are the declarations of two NSString functions not functions that you need to create. As NSMutableString is a subclass of NSString you can use those functions on your convertMutable variable.

Another thing you need to deal with is creating a name for the file you want to save, currently you have pasted in the line above that gets the Documents directory but does not have a file name. You will need to devise a way to create a unique filename each time you save a CSV file and add that name to the end of path. Then you can use writeToFile… or writeToURL… to write the string to the desired location.

If you find you don't fully comprehend the code you are adding then consider getting a book or finding classes about Swift (Coursera.org has a class that may be of use). There are plenty of resources out there learn the basics of software development and Swift, it will take effort and time but it will be worth it in the end if this is something you want to pursue.

How do I write a new row a CSV file in documents directory in swift 5?

Solved using Warren's advice:

    var logFile: URL? {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil }
let fileName = "symptom_data.csv"
return documentsDirectory.appendingPathComponent(fileName)
}

This function checks if the file exists, if not it makes it like in my original question, and if so uses FileManager to add a new line.

    func createCSV() {
guard let logFile = logFile else {
return
}

guard let data = ("\(symptomData.symptom),\(symptomData.severity),\(symptomData.comment),\(symptomData.timestamp)\n").data(using: String.Encoding.utf8) else { return }

if FileManager.default.fileExists(atPath: logFile.path) {
if let fileHandle = try? FileHandle(forWritingTo: logFile) {
fileHandle.seekToEndOfFile()
fileHandle.write(data)
fileHandle.closeFile()
}
} else {
var csvText = "Symptom,Severity,Comment,Time\n"

let newLine = "\(symptomData.symptom),\(symptomData.severity),\(symptomData.comment),\(symptomData.timestamp)\n"
csvText.append(newLine)

do {
try csvText.write(to: logFile, atomically: true, encoding: String.Encoding.utf8)

} catch {
print("Failed to create file")
print("\(error)")
}
print(logFile ?? "not found")
}
}

Exporting Data Via CSV File

I think this CodableCSV project will be a really good starting point for you.

Here's the end of my modified createCsv():

let myRows = [
["Quanity", "Item", "Cost", "Total", "Total (USD)"],
[sCount, barName, sCost, sTotal, newTotal]
]

do {
let string = try CSVWriter.encode(rows: myRows, into: String.self)
print(string)
return MessageDocument(message: string)
} catch {
fatalError("Unexpected error encoding CSV: \(error)")
}

When I click on the 'Export' button, I see, from that print statement:

Quanity,Item,Cost,Total,Total (USD)
"1,600",ChocoBar,€4.95,"€7,920.00","$8,954.27"

You're going to need to be deliberate about adding title, subTitle, "Sale in Dollars" because the rows they're on need to have the same number of columns as your data—CSV isn't Excel in this regard; where in Excel you can put data in any cell, no imposed structure—so something like:

let myRows = [
["Henry's Chocolate Sales Frankfurt", "", "", ""], // 3 empty (placeholder) columns
...
]

Swift - Create CSV file with text/csv MIME type

Thank you everyone for your input!

I received some help from the Apple Developer Forums:
https://developers.apple.com/forums/thread/710757?page=1#721303022

Changing the line-break characters from \n to \r\n did the trick! The file command now shows the file's MIME type as text\csv.

How to create a CSV file from Core Data (swift)

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.



Related Topics



Leave a reply



Submit