I have successfully imported a simple two-column CSV file using DataFrame. Now I want to turn the two cells in each row into strings. The left or right side will occasionally be missing a value; see print output. table
When I try to process a row with a nil cell, the program crashes with "Fatal error: Unexpectedly found nil while unwrapping an Optional value"
So my question is, how do I turn the cells into strings ("" if Nil) safely?
Complete ContentView code below, thanks in advance for any help.
import SwiftUI
import TabularData
struct ContentView: View {
@State var openFile = false
var body: some View {
VStack {
Button(action: {openFile.toggle()}, label: {
Text("Open")
})
}.fileImporter(
isPresented: $openFile,
allowedContentTypes: [.commaSeparatedText],
allowsMultipleSelection: false) { (result) in
do {
let fileURL = try result.get().first
if fileURL!.startAccessingSecurityScopedResource() {
print(fileURL!)
importTable(url: fileURL!)
}
fileURL!.stopAccessingSecurityScopedResource()
} catch {
print("Unable to read file contents")
print(error.localizedDescription)
}
}
}
func importTable(url: URL) {
var importerTable: DataFrame = [:]
let options = CSVReadingOptions(hasHeaderRow: false, delimiter: ",")
do {
importerTable = try DataFrame(
contentsOfCSVFile: url,
options: options)
} catch {
print("ERROR reading CSV file")
print(error.localizedDescription)
}
print("\(importerTable)")
importerTable.rows.forEach { row in
let leftString = row[0]! as! String
let rightString = row[1]! as! String
print("Left: \(leftString) Right: \(rightString)")
}
}
}
CodePudding user response:
A fileImporter
context is quite insecure, any carelessly written exclamation mark can crash the app.
First you have to check fileURL
and abort the import if it's nil
guard let fileURL = try result.get().first else { return }
if fileURL.startAccessingSecurityScopedResource() {
print(fileURL)
importTable(url: fileURL)
}
fileURL.stopAccessingSecurityScopedResource()
In importTable
you must not continue after a do - catch
if an error is thrown and you have to check if the CSV file contains two items per row. On the other hand CSV is String
by definition, the (force) cast to String
is redundant.
func importTable(url: URL) {
var importerTable: DataFrame = [:]
let options = CSVReadingOptions(hasHeaderRow: false, delimiter: ",")
do {
importerTable = try DataFrame(
contentsOfCSVFile: url,
options: options)
print("\(importerTable)")
importerTable.rows.forEach { row in
if row.count > 1 {
let leftString = row[0]
let rightString = row[1]
print("Left: \(leftString) Right: \(rightString)")
}
}
} catch {
print("ERROR reading CSV file")
print(error.localizedDescription)
}
}