> Как понять, что файл на сервере изменился при сохранении имени и пути? (iOS, Swift)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: bnine

Стек: iOS, Swift

> Пример ответа

Короткий ответ

Если имя и путь файла не меняются, но содержимое обновляется, нужно сравнивать метаданные: modification date, file size или content hash. На iOS надёжнее всего использовать FileManager для получения атрибутов (modificationDate, size) и, при необходимости, вычислять hash содержимого. Для постоянного мониторинга - DispatchSourceFileSystemObject с событиями .write или .extend. Важно учитывать кэширование и задержки файловой системы.

Подробное объяснение

Файл на сервере - это абстракция: в мобильном контексте речь обычно идёт о локальной копии, полученной с сервера, либо о файле в sandbox приложения. Если путь и имя неизменны, изменение можно детектировать тремя способами:

  • modification date - самый простой и дешёвый. Но дата может не обновиться при записи с тем же размером или при копировании с сохранением атрибутов.
  • file size - быстро, но не различает замену содержимого с тем же размером.
  • content hash (MD5/SHA) - самый надёжный, но требует чтения всего файла. Для больших файлов - дорого.

На iOS для локального мониторинга изменений используют DispatchSourceFileSystemObject - он слушает события ядра (.write, .extend, .delete, .rename). Это эффективнее polling’а, но требует аккуратного управления жизненным циклом источника.

Для удалённого файла на сервере (HTTP) - задача сводится к сравнению ETag, Last-Modified или Content-Length в заголовках ответа. Если сервер не отдаёт эти заголовки, придётся скачивать файл и сравнивать hash.

Важный нюанс: на iOS файловая система может кэшировать атрибуты, поэтому после записи нужно сбрасывать кэш (FileManager не кэширует, но URLResourceValues могут быть устаревшими). Также стоит учитывать, что modificationDate имеет точность до секунды - при быстрых последовательных записях можно пропустить изменение.

На практике

Для локального файла в приложении:

  1. Получить атрибуты через FileManager.attributesOfItem(atPath:).
  2. Сравнить modificationDate и size с сохранёнными значениями.
  3. Если нужна гарантия - вычислить hash (например, SHA256) и сравнить.
  4. Для непрерывного наблюдения - создать DispatchSourceFileSystemObject на дескрипторе файла.

Для удалённого файла:

  • Использовать URLSession с conditional GET: отправлять If-Modified-Since или If-None-Match и проверять статус 304 Not Modified.
  • Если сервер не поддерживает - скачивать и сравнивать hash.

Типичный сценарий в iOS: приложение хранит кэш файла, и при каждом запуске проверяет, не изменился ли он на сервере. Здесь лучше полагаться на ETag/Last-Modified, а не на локальные атрибуты.

Пример кода

SWIFT
import Foundation
func fileChanged(atPath path: String,
previousDate: Date?,
previousSize: Int?) -> Bool {
let attrs = try? FileManager.default.attributesOfItem(atPath: path)
guard let attrs else { return true }
let currentDate = attrs[.modificationDate] as? Date
let currentSize = (attrs[.size] as? NSNumber)?.intValue
if let previousDate, let currentDate,
let previousSize, let currentSize {
return currentDate != previousDate || currentSize != previousSize
}
return true
}
// Мониторинг через DispatchSource
func watchFile(atPath path: String, onChange: @escaping () -> Void) -> DispatchSourceFileSystemObject? {
let fd = open(path, O_EVTONLY)
guard fd >= 0 else { return nil }
let source = DispatchSource.makeFileSystemObjectSource(
fileDescriptor: fd,
eventMask: [.write, .extend, .delete, .rename],
queue: .main
)
source.setEventHandler {
onChange()
}
source.setCancelHandler {
close(fd)
}
source.resume()
return source
}

Как отвечать на собеседовании

Начни с уточнения: "Речь о локальном файле или удалённом?" - это покажет системное мышление. Затем перечисли три подхода (дата, размер, hash) и объясни trade-off между ними. Для локального - упомяни DispatchSourceFileSystemObject, для удалённого - ETag/Last-Modified. Подчеркни, что выбор зависит от требований к надёжности и производительности. Приведи пример, когда дата и размер недостаточны (замена содержимого с тем же размером). Если спросят про кэширование - скажи, что на iOS атрибуты читаются напрямую из файловой системы, но стоит избегать частого чтения больших файлов.

Что проверяет интервьюер

  • Понимание разницы между метаданными и содержимым файла.
  • Знание механизмов мониторинга файловой системы на iOS.
  • Умение оценивать надёжность и стоимость каждого подхода.
  • Понимание сетевых протоколов для удалённых файлов (HTTP conditional requests).
  • Способность предложить решение под конкретный сценарий, а не универсальный ответ.

Типичные ошибки

  • Предложение только сравнивать modificationDate без учёта ограничений точности.
  • Игнорирование случая, когда файл заменяется целиком (rename + write) - событие .delete или .rename может не прийти.
  • Использование FileManager для постоянного мониторинга через polling - это неэффективно.
  • Забывают про O_EVTONLY при открытии файла для наблюдения - иначе дескриптор блокирует запись.
  • Для удалённого файла предлагают скачивать целиком каждый раз, не упоминая conditional GET.
  • Не учитывают, что hash всего файла - дорогая операция для больших файлов, и предлагают её как единственный вариант.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью