Работа с CSV в Swift (iOS/macOS) — библиотеки и примеры кода

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Работа с CSV в Swift (iOS/macOS): библиотеки, парсинг и примеры

CSV (Comma-Separated Values) — один из самых распространённых форматов для обмена табличными данными. В экосистеме Apple (iOS, macOS, watchOS) работа с CSV часто требуется для импорта/экспорта данных, загрузки конфигураций или обработки логов. В этой статье мы рассмотрим основные подходы к работе с CSV в Swift: ручной парсинг, популярные библиотеки (SwiftCSV и CodableCSV) и примеры кода с пояснениями.

1. Почему стоит использовать библиотеки для CSV?

Стандартная библиотека Foundation не предоставляет готовых средств для работы с CSV. Ручной парсинг (разделение по запятым, обработка кавычек, экранирование символов) быстро становится громоздким и подвержен ошибкам. Библиотеки решают следующие задачи:

  • Корректная обработка кавычек и экранированных символов.
  • Поддержка разных разделителей (запятая, табуляция, точка с запятой).
  • Чтение больших файлов потоково (без загрузки в память целиком).
  • Интеграция с Codable для автоматической десериализации.

2. Библиотека SwiftCSV

SwiftCSV — лёгкая библиотека для чтения и записи CSV. Поддерживает строки, словари и вложенные массивы. Установка через Swift Package Manager:

// Package.swift
dependencies: [
    .package(url: "https://github.com/swiftcsv/SwiftCSV.git", from: "0.9.0")
]

Пример чтения CSV из файла:

import SwiftCSV

guard let path = Bundle.main.path(forResource: "data", ofType: "csv") else {
    fatalError("CSV file not found")
}
let csv = try CSV(url: URL(fileURLWithPath: path))

// Доступ к строкам по заголовкам
for row in csv.rows {
    let name = row["name"] ?? ""
    let age = row["age"] ?? ""
    print("\(name) is \(age) years old")
}

// Доступ к столбцам
let allNames = csv.columns["name"] ?? []
print(allNames)

Запись CSV: библиотека не предоставляет встроенного метода записи, но вы можете сформировать строку вручную:

func writeCSV(rows: [[String: String]], headers: [String]) -> String {
    var csvString = headers.joined(separator: ",") + "\n"
    for row in rows {
        let values = headers.map { row[$0] ?? "" }
        csvString += values.joined(separator: ",") + "\n"
    }
    return csvString
}

let csvData = writeCSV(rows: data, headers: ["name", "age"])
try csvData.write(to: url, atomically: true, encoding: .utf8)

3. Библиотека CodableCSV

CodableCSV — мощная библиотека, поддерживающая Codable, кастомные разделители, потоковое чтение и настраиваемую кодировку. Идеально подходит для сложных проектов.

Установка:

// Package.swift
dependencies: [
    .package(url: "https://github.com/dehesa/CodableCSV.git", from: "0.9.0")
]

Пример чтения с автоматической десериализацией в модель:

import CodableCSV

struct Person: Codable {
    let name: String
    let age: Int
    let email: String?
}

let data = try Data(contentsOf: url)
let decoder = CSVDecoder { config in
    config.headerStrategy = .firstLine  // первая строка — заголовки
    config.delimiter = .comma
}
let people = try decoder.decode([Person].self, from: data)

for person in people {
    print(person.name, person.age)
}

Запись CSV с помощью CodableCSV:

let encoder = CSVEncoder { config in
    config.headerStrategy = .firstLine
    config.delimiter = .comma
}
let data = try encoder.encode(people, into: Data.self)
try data.write(to: url, options: .atomic)

4. Ручной парсинг (без библиотек)

Если проект небольшой и вы хотите избежать зависимостей, можно реализовать простой парсер. Важно: такой код не обрабатывает все edge-кейсы (кавычки внутри полей, многострочные поля).

func parseCSV(_ content: String) -> [[String]] {
    var rows: [[String]] = []
    let lines = content.components(separatedBy: .newlines)
    for line in lines where !line.trimmingCharacters(in: .whitespaces).isEmpty {
        let fields = line.components(separatedBy: ",")
        rows.append(fields)
    }
    return rows
}

// Использование
let csvContent = try String(contentsOf: url, encoding: .utf8                    

Рекомендаций