CodableCSV

repository·master·Indexed 19 days ago

https://github.com/dehesa/codablecsv

A multiplatform Swift library for reading and writing CSV files. It provides low-level imperative processing via CSVReader and CSVWriter, as well as high-level declarative encoding and decoding using Swift's Codable protocol through CSVDecoder and CSVEncoder. The library supports various configuration options for delimiters, encoding, and data strategies, and includes lazy processing for memory-efficient handling of large datasets.

Tokens
4.5K
Snippets
16
Records
19
Agent score
67%

What's inside CodableCSV

  1. Overview of CodableCSV usage patterns

    master

    CodableCSV provides two primary ways to interact with CSV data:

    1. Imperative Reader/Writer: Use this for low-level, row-by-row, and field-by-field processing. This is useful when you need fine-grained control over the parsing or writing process.
    2. Declarative Decoder/Encoder: Use this to leverage Swift's Codable protocol. This allows you to map CSV rows directly to Swift structs or classes, providing a high-level, type-safe interface.
  2. Use integer CodingKeys for better CSV performance

    master

    By default, CSVDecoder matches Codable property names to the CSV header row. If your CSV does not have a header row, or if you want to improve performance, you can use integer-based CodingKeys to map properties directly to field indices. This allows the decoder to skip the string-matching step.

    struct Student: Codable {
        var name: String
        var age: Int
        var hasPet: Bool
    
        private enum CodingKeys: Int, CodingKey {
            case name = 0
            case age = 1
            case hasPet = 2
        }
    }
  3. Find help and documentation for CodableCSV

    master

    To learn how to use CodableCSV, consult the following resources:

    • README.md: The primary source of information, covering everything from basic usage to performance optimization tips. Look for the right-pointing carets (▶) to expand detailed sections.
    • GitHub Issues: Search for existing questions by looking for issues labeled with question.
    • New Questions: If you cannot find an answer, you can create a new issue on GitHub and label it as question.
  4. Install CodableCSV via SPM or CocoaPods

    master

    You can integrate CodableCSV into your Swift project using either Swift Package Manager (SPM) or CocoaPods.

    Swift Package Manager (SPM)

    Add the dependency to your Package.swift file:

    CocoaPods

    Add the following line to your Podfile:

    // swift-tools-version:5.1
    import PackageDescription
    
    let package = Package(
        /* Your package name, supported platforms, and generated products go here */
        dependencies: [
            .package(url: "https://github.com/dehesa/CodableCSV.git", from: "0.6.7")
        ],
        targets: [
            .target(name: /* Your target name here */, dependencies: ["CodableCSV"])
        ]
    )
    pod 'CodableCSV', '~> 0.6.7'
  5. Learn about Swift's Codable interface

    master

    Since CodableCSV relies on Swift's Codable protocol, you may need to understand how encoding and decoding custom types work. Recommended resources include:

    • Apple's Documentation: Official guides on encoding and decoding custom types.
    • Flight School: The 'Guide to Swift Codable' by Mattt.
    • Swift Forums: For complex queries, visit the Swift forums (specifically the 'Using Swift' category) and use the codable search tag.
    • CodableCSV README: Contains specific usage examples related to CSV serialization.
  6. Configure CSVReader

    master

    You can configure a CSVReader during initialization using a closure. Available properties include:

    • encoding: The String.Encoding (e.g., .utf8). If nil, it attempts to detect via BOM, defaulting to .utf8.
    • delimiters: A tuple specifying field and row delimiters (e.g., (field: ",", row: "\n")).
    • escapingStrategy: The Unicode scalar used to escape fields (default is "").
    • headerStrategy: Determines how headers are handled (e.g., .firstLine or .none).
    • trimStrategy: A set of characters to trim from the start and end of parsed fields.
    • presample: If true, the entire CSV is loaded into memory before parsing begins.
    let reader = CSVReader(input: ...) {
        $0.encoding = .utf8
        $0.delimiters.row = "\r\n"
        $0.headerStrategy = .firstLine
        $0.trimStrategy = .whitespaces
    }
  7. Configure CSVEncoder

    master

    Configure CSVEncoder during initialization or after creation.

    Available configuration keys include:

    • headers: Required if using keyed encoding containers.
    • nilStrategy: How nil is represented in CSV (default: .empty).
    • boolStrategy: How Bool encodes to String (default: .deferredToString).
    • nonConformingFloatStrategy: Handling of NaN and infinity (default: .throw).
    • decimalStrategy: How Decimal encodes to String (default: .locale).
    • dateStrategy: How Date encodes to String (default: .deferredToDate).
    • dataStrategy: How Data encodes to String (default: .base64).
    • bufferingStrategy: Controls KeyedEncodingContainer behavior (default: .keepAll).
    let encoder = CSVEncoder {
        $0.headers = ["name", "age", "hasPet"]
        $0.delimiters = (field: ";", row: "\r\n")
        $0.dateStrategy = .iso8601
        $0.bufferingStrategy = .sequential
        $0.floatStrategy = .convert(positiveInfinity: "∞", negativeInfinity: "-∞", nan: "≁")
        $0.dataStrategy = .custom({
            let string = customTransformation(data)
            var container = try $0.singleValueContainer()
            try container.encode(string)
        })
    }
  8. Configure CSVWriter

    master

    You can configure a CSVWriter during initialization using a closure. Available properties include:

    • delimiters: A tuple specifying field and row delimiters.
    • escapingStrategy: The Unicode scalar used to escape fields (default is .doubleQuote).
    • headers: An array of strings to be written as the first row.
    • encoding: The String.Encoding for the output.
    • bomStrategy: Determines if a Byte Order Marker is included (.always, .never, or .convention).
    let writer = CSVWriter(fileURL: url) {
        $0.delimiters.row = "\r\n"
        $0.headers = ["Name", "Age", "Pet"]
        $0.encoding = .utf8
        $0.bomStrategy = .never
    }
  9. Configure CSVDecoder

    master

    Configure CSVDecoder during initialization via a closure or by setting properties directly (thanks to @dynamicMemberLookup).

    Available configuration keys include:

    • nilStrategy: How nil (absence of value) is represented (default: .empty).
    • boolStrategy: How strings decode to Bool (default: .insensitive).
    • nonConformingFloatStrategy: Handling of NaN and infinity (default: .throw).
    • decimalStrategy: How strings decode to Decimal (default: .locale).
    • dateStrategy: How strings decode to Date (default: .deferredToDate).
    • dataStrategy: How strings decode to Data (default: .base64).
    • bufferingStrategy: Controls KeyedDecodingContainer behavior (default: .keepAll).
    let decoder = CSVDecoder {
        $0.encoding = .utf8
        $0.delimiters.field = "\t"
        $0.headerStrategy = .firstLine
        $0.bufferingStrategy = .sequential
        $0.decimalStrategy = .custom({
            let value = try Float(from: $0)
            return Decimal(value)
        })
    }
  10. Handle CSVError

    master

    Imperative operations in CodableCSV throw CSVError. This error type provides detailed diagnostic information to help resolve issues like invalid configuration or malformed CSV data.

    Key properties of CSVError include:

    • type: The error category.
    • failureReason: A description of what went wrong.
    • helpAnchor: Advice on how to fix the error.
    • errorUserInfo: Associated arguments.
    • underlyingError: The original error that caused the failure, if applicable.
    • localizedDescription: A human-readable error string.
    do {
        let writer = try CSVWriter()
        try writer.write(row: ["data"])
        try writer.endEncoding()
    } catch let error as CSVError {
        print("Reason: \(error.failureReason)")
        print("Help: \(error.helpAnchor)")
    }
  11. Generate type-safe headers for CSVEncoder

    master

    To ensure your CSV headers match your Swift model, you can use CaseIterable on a String-based CodingKeys enum to automatically populate the encoder's headers configuration.

    struct Student: Codable {
        var name: String
        var age: Int
        var hasPet: Bool
    
        enum CodingKeys: String, CodingKey, CaseIterable {
            case name, age, hasPet
        }
    }
    
    let encoder = CSVEncoder {
        $0.headers = Student.CodingKeys.allCases.map { $0.rawValue }
    }