Работа с CSV в Swift (iOS/macOS): от простого к сложному
CSV (Comma-Separated Values) — один из самых распространённых форматов для обмена табличными данными. Он используется для экспорта из Excel, Google Sheets, баз данных и простых логов. В экосистеме Swift (iOS, macOS, watchOS, tvOS) нет встроенного модуля для работы с CSV, но существует несколько проверенных библиотек, которые значительно упрощают этот процесс. В этой статье мы рассмотрим лучшие инструменты и напишем несколько примеров кода.
Почему не стоит писать парсер CSV вручную?
На первый взгляд CSV кажется простым: разделили строку по запятым — и готово. Однако на практике возникают сложности:
- Поля могут содержать запятые внутри (например,
"Smith, John"). - Поля могут быть обёрнуты в кавычки, а внутри кавычек — экранированные кавычки (
""). - Разные системы используют разные разделители (запятая, точка с запятой, табуляция).
- Проблемы с кодировками (UTF-8, UTF-16, Windows-1251).
Поэтому для production-кода настоятельно рекомендуется использовать готовую библиотеку.
Лучшие библиотеки для CSV в Swift
1. SwiftCSV (от Naoto Kaneko)
Одна из самых популярных и лёгких библиотек. Поддерживает чтение и запись, автоматически обрабатывает кавычки и экранирование. Работает через Swift Package Manager (SPM) и CocoaPods.
// Подключение через SPM: .package(url: "https://github.com/naoty/SwiftCSV.git", from: "0.9.0")
2. CodableCSV (от Dehesa)
Современная библиотека, которая интегрируется с протоколом Codable. Позволяет парсить CSV напрямую в структуры Swift. Идеально для проектов, где данные имеют строгую типизацию.
// Подключение: .package(url: "https://github.com/dehesa/CodableCSV.git", from: "0.6.0")
3. CSwift
Простая и быстрая библиотека с минимальным API. Хорошо подходит для небольших проектов или быстрого прототипирования.
Пример 1: Чтение CSV с помощью SwiftCSV
Допустим, у нас есть файл users.csv:
id,name,email
1,Иван Иванов,ivan@example.com
2,Мария Петрова,maria@example.com
3,Джон Доу,"john.doe@example.com"
Код для чтения:
import SwiftCSV
guard let csvURL = Bundle.main.url(forResource: "users", withExtension: "csv") else {
print("Файл не найден")
return
}
do {
let csv = try CSV(url: csvURL)
// Доступ к строкам как к массиву словарей
for row in csv.rows {
if let id = row["id"], let name = row["name"], let email = row["email"] {
print("ID: \(id), Имя: \(name), Email: \(email)")
}
}
// Или доступ по столбцам
print("Все имена: \(csv.columns["name"] ?? [])")
} catch {
print("Ошибка парсинга: \(error)")
}
Пример 2: Запись CSV с помощью SwiftCSV
import SwiftCSV
let headers = ["Name", "Age", "City"]
let rows = [
["Анна", "28", "Москва"],
["Борис", "35", "Санкт-Петербург"],
["Виктор", "22", "Новосибирск"]
]
do {
let csv = try CSV(string: "", headers: headers, rows: rows)
let csvString = csv.output // Получаем строку CSV
print(csvString)
// Сохраняем в файл
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsPath.appendingPathComponent("output.csv")
try csvString.write(to: fileURL, atomically: true, encoding: .utf8)
print("Файл сохранён: \(fileURL)")
} catch {
print("Ошибка записи: \(error)")
}
Пример 3: CodableCSV — парсинг в структуры
Этот пример показывает, как легко интегрировать CSV с моделями данных:
import CodableCSV
struct User: Decodable {
let id: Int
let name: String
let email: String
}
guard let csvURL = Bundle.main.url(forResource: "users", withExtension: "csv") else { return }
do {
let data = try Data(contentsOf: csvURL)
let decoder = CSVDecoder { $0.headerStrategy = .firstLine }
let users = try decoder.decode([User].self, from: data)