Работа с 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