Parsing CSV file in Swift
You are attempting to parse the file path rather than the contents of the file.
If you replace
let content = String(contentsOfURL: contentsOfURL, encoding: encoding, error: error)
with:
if let data = NSData(contentsOfURL: contentsOfURL) {
if let content = NSString(data: data, encoding: NSUTF8StringEncoding) {
//existing code
}
}
then the code will work for your example file.
Here's a foolproof way of parsing a CSV file into your swift code (I'm using Swift 5 here). I'll talk through each step for any beginners in the room.
Let's assume your CSV file looks like this:
Firstname,Last name,Age,Registered
Duncan,Campbell,40,True
Tobi,Dorner,36,False
Saskia,Boogarts,29,True
1). We need a struct (or Object) to hold each row of data. Let's use this:
struct Person {
var firstName: String
var lastName: String
var age: Int
var isRegistered: Bool
}
2) We also need a variable which holds an array of each Person
.
var people = [Person]()
3) Now - add the CSV file to your XCode project (you can drag and drop this into your project). Make sure it has a sensible name (e.g. data.csv).
4) You now need to "locate" the data that you want to use. Create a filepath which tells the code where to find your csv file:
guard let filepath = Bundle.main.path(forResource: "data", ofType: "csv") else {
return
}
5) Now we want to read the contents of this file. First let's convert the whole file into one long String.
var data = ""
do {
data = try String(contentsOfFile: filepath)
} catch {
print(error)
return
}
6) We now have a string with all out data in one line. We want to split this up into an array of strings, one string for each row in the data. (BTW, the \n
means "new line")
let rows = data.components(separatedBy: "\n")
7) We now have an array with 4 rows - one for the header titles, and 3 rows for each person in the data. We're not interested in the first header row, so we can remove that one. (Ignore this step if you don't have a header row in your data)
rows.removeFirst()
8) Now loop around each of rows. Each row is currently a string (e.g. Duncan,Campbell,40,True
) but we want to split it into an array of each of its 4 columns.
for row in rows {
let columns = row.components(separatedBy: ",")
9) We now have a array columns
which has 4 strings in it. Let's convert each column to the correct data type.
let firstName = columns[0]
let lastName = columns[1]
let age = Int(columns[2]) ?? 0
let isRegistered = columns[3] == "True"
10) And now we can create the Person object, and append it to our array.
let person = Person(firstName: firstName, lastName: lastName, age: age, isRegistered: isRegistered)
people.append(person)
Here's the full code for anyone who is interested:
struct Person {
var firstName: String
var lastName: String
var age: Int
var isRegistered: Bool
}
var people = [Person]()
func convertCSVIntoArray() {
//locate the file you want to use
guard let filepath = Bundle.main.path(forResource: "data", ofType: "csv") else {
return
}
//convert that file into one long string
var data = ""
do {
data = try String(contentsOfFile: filepath)
} catch {
print(error)
return
}
//now split that string into an array of "rows" of data. Each row is a string.
var rows = data.components(separatedBy: "\n")
//if you have a header row, remove it here
rows.removeFirst()
//now loop around each row, and split it into each of its columns
for row in rows {
let columns = row.components(separatedBy: ",")
//check that we have enough columns
if columns.count == 4 {
let firstName = columns[0]
let lastName = columns[1]
let age = Int(columns[2]) ?? 0
let isRegistered = columns[3] == "True"
let person = Person(firstName: firstName, lastName: lastName, age: age, isRegistered: isRegistered)
people.append(person)
}
}
}