Avatar ·

Чтение и запись строки в текстовый файл на Swift

Всем привет! Я новичок в Swift и пытаюсь разобраться с чтением и записью строк в текстовые файлы. Перепробовал кучу вариантов из интернета, но постоянно получаю ошибки или nil. Вот что я пробовал: ```swift let path = "/Users/me/Documents/test.txt" let content = try String(contentsOfFile: path) print(content) ``` Выдаёт ошибку: "The file couldn’t be opened because there is no such file." А для записи я делал так: ```swift let text = "Hello, world!" try text.write(toFile: path, atomically: true, encoding: .utf8) ``` Тоже ошибка: "You don’t have permission to save the file." Я пробовал использовать FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first, но всё равно путаюсь. Подскажите, как правильно читать и записывать строки в текстовые файлы на Swift для iOS и macOS? Нужны простые и понятные примеры, желательно с обработкой ошибок. Заранее спасибо! <<>>

Правильное чтение и запись текстовых файлов в Swift

Проблема, с которой вы столкнулись, типична для начинающих разработчиков. Давайте разберёмся по порядку. Главная ошибка — попытка использовать абсолютные пути, которые не работают в песочнице (sandbox) iOS или требуют прав на macOS.

Для iOS (и macOS с sandbox)

Используйте FileManager для получения правильной директории:

import Foundation

// Получаем путь к Documents
guard let documentsDirectory = FileManager.default.urls(
    for: .documentDirectory,
    in: .userDomainMask
).first else {
    fatalError("Не удалось получить Documents directory")
}

let fileURL = documentsDirectory.appendingPathComponent("myFile.txt")

// ЗАПИСЬ
let textToWrite = "Hello, Swift!"
do {
    try textToWrite.write(to: fileURL, atomically: true, encoding: .utf8)
    print("Файл записан: \(fileURL.path)")
} catch {
    print("Ошибка записи: \(error.localizedDescription)")
}

// ЧТЕНИЕ
do {
    let readText = try String(contentsOf: fileURL, encoding: .utf8)
    print("Прочитано: \(readText)")
} catch {
    print("Ошибка чтения: \(error.localizedDescription)")
}

Для macOS (без sandbox, с произвольным путём)

Если вы уверены, что файл существует и у вас есть права:

let path = "/Users/me/Documents/test.txt"
let fileURL = URL(fileURLWithPath: path)

// Запись
let text = "Hello, macOS!"
do {
    try text.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
    print("Ошибка: \(error)")
}

// Чтение
do {
    let content = try String(contentsOf: fileURL, encoding: .utf8)
    print(content)
} catch {
    print("Ошибка: \(error)")
}

Ключевые моменты

  • URL vs String path: Всегда используйте URL вместо строки пути — это безопаснее и переносимее.
  • Песочница (sandbox): На iOS приложение видит только свою директорию Documents. На macOS в песочнице — аналогично.
  • Обработка ошибок: Обязательно используйте do-catch или try? для обработки возможных ошибок.
  • Кодировка: Всегда указывайте .utf8 для совместимости.
  • atomically: true — гарантирует, что файл не повредится при сбое записи.

Дополнительно: проверка существования файла

let fileManager = FileManager.default
if fileManager.fileExists(atPath: fileURL.path) {
    print("Файл существует")
} else {
    print("Файла нет, создаём...")
}

Для быстрого прототипирования (playground)

В Xcode Playground можно использовать NSTemporaryDirectory():

let tempDir = NSTemporaryDirectory()
let tempFile = tempDir + "temp.txt"
try? "Test".write(toFile: tempFile, atomically: true, encoding: .utf8)
let result = try? String(contentsOfFile: tempFile)
print(result ?? "nil")

Теперь вы знаете правильный подход. Главное — всегда работать через FileManager и использовать URL.

<<>>

Альтернативный подход: использование FileHandle и Data

Если вам нужно больше контроля над процессом (например, дописывать в конец файла), используйте FileHandle:

import Foundation

let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    .appendingPathComponent("log.txt")

// Запись (создаёт файл, если его нет)
let data = "Новая строка\n".data(using: .utf8)!
FileManager.default.createFile(atPath: fileURL.path, contents: data, attributes: nil)

// Дозапись в конец
if let handle = try? FileHandle(forWritingTo: fileURL) {
    handle.seekToEndOfFile()
    handle.write(data)
    handle.closeFile()
}

// Чтение
if let handle = try? FileHandle(forReadingFrom: fileURL) {
    let readData = handle.readDataToEndOfFile()
    let text = String(data: readData, encoding: .utf8)
    print(text ?? "Ошибка чтения")
    handle.closeFile()
}

Этот метод удобен для логов или потоковой записи. Однако для простых операций первый ответ (с write и String(contentsOf:)) предпочтительнее — он короче и понятнее.