Home > Blockchain >  Safe handling of Apple TabularData table data in Swift
Safe handling of Apple TabularData table data in Swift

Time:05-10

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