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