NSFileCoordinator — координація доступу до файлів в iOS

Автор: IT Sectr Опубліковано: 2026-07-11 Час читання: 7 хв

NSFileCoordinator — це клас Foundation в iOS та macOS, що забезпечує безпечний доступ до файлів при одночасній роботі кількох потоків, процесів або розширень. За даними Apple Developer Documentation, 2024, NSFileCoordinator запобігає станам гонки (race conditions) при читанні та запису файлів, гарантуючи, що жоден процес не прочитає дані в момент їх зміни іншим. Координатор використовується в iCloud Drive, File Provider Extension та будь-яких багатопотокових файлових операціях.

Головне

  • NSFileCoordinator — клас для безпечного доступу до файлів з кількох потоків та процесів
  • Координаційні блоки (reading/writing intent) оголошують тип операції до її виконання
  • Запобігання станам гонки — основне завдання координатора при паралельному доступі
  • Підтримка File Provider — координатор обов'язковий при роботі з файлами iCloud Drive та розширень
  • NSFilePresenter — протокол для отримання сповіщень про зміни файлів з інших процесів

Що таке NSFileCoordinator?

NSFileCoordinator — це механізм синхронізації доступу до файлів на рівні операційної системи, представлений Apple в iOS 5 та macOS 10.7 Lion. На відміну від традиційних блокувань (NSLock, pthread_mutex), координатор працює на рівні файлової системи та може узгоджувати доступ між різними процесами, а не лише між потоками одного застосунку.

Необхідність в NSFileCoordinator виникає через архітектуру Sandbox в iOS: кожен процес (застосунок, розширення, системний сервіс) працює в ізольованому середовищі з власним доступом до файлів. Коли кілька процесів намагаються одночасно читати та писати один файл (наприклад, при синхронізації iCloud Drive), без координатора виникають стани гонки: процес A читає файл в момент, коли процес B вже частково його перезаписав.

За даними WWDC 2023, Apple наполегливо рекомендує використовувати NSFileCoordinator для всіх операцій з файлами в Ubiquity container (iCloud Drive) та при роботі з File Provider Extension. Ігнорування координації — одна з частих причин пошкодження даних та невідтворюваних багів в iOS-застосунках.

Типи координаційних намірів

Координаційний намір (NSFileCoordinator.ReadingIntent / WritingIntent) — це об'єкт, що декларує тип операції, яку планує виконати потік або процес. Координатор використовує ці наміри для визначення порядку доступу та вирішення конфліктів.

Тип наміруОписКоли використовувати
ReadingIntentЧитання файлу без змінВідкриття документа, завантаження даних
WritingIntentЗапис з можливою зміною вмістуЗбереження документа, редагування
ReadingIntent(URL, options: .withoutChanges)Читання без відстеження змінШвидкий перегляд вмісту
WritingIntent(URL, options: .contentIndependentMetadataOnly)Зміна лише метаданихОновлення дати або атрибутів
WritingIntent(URL, options: .forDeleting)Видалення файлуВидалення документа користувачем

Правила координації: множинні одночасні читання дозволені (якщо немає активного запису), запис ексклюзивний — жодне читання або запис не допускаються під час операції запису. Це відповідає моделі readers-writer lock, але з додатковою підтримкою міжпроцесної координації через launchd та XPC.

Важливий нюанс: NSFileCoordinator не запобігає доступу до файлу через звичайні NSData або FileManager — він координує лише ті операції, які явно обгорнуті в блоки координації. Якщо інший потік звертається до файлу напряму, минаючи координатор, виникають ті самі стани гонки, які координатор покликаний запобігати.

NSFileCoordinator в дії: приклади коду

Базовий патерн використання NSFileCoordinator складається з трьох кроків: створити екземпляр координатора, оголосити намір (читання або запис) та виконати операцію всередині блоку координації. Координатор гарантує, що жоден інший координатор не буде одночасно працювати з тим самим файлом.

swift
import Foundation

let coordinator = NSFileCoordinator()
let fileURL = getDocumentURL()

// Safe reading
let readIntent = NSFileCoordinator
    .ReadingIntent(url: fileURL)
var content: Data?
var readError: NSError?

coordinator.coordinate(with: readIntent) { error in
    if let error = error {
        readError = error
        return
    }
    content = try? Data(contentsOf: fileURL)
}

// Safe writing
let writeIntent = NSFileCoordinator
    .WritingIntent(url: fileURL)

coordinator.coordinate(with: writeIntent) { error in
    guard error == nil else { return }
    do {
        try newData.write(to: fileURL)
    } catch {
        Logger.storage.error(
            "Write failed: \(error)"
        )
    }
}

Пакетна операція — координатор може обробляти кілька файлів в одній операції, використовуючи масив намірів. Це зручно для переміщення, копіювання або видалення набору файлів як єдиної транзакції. Якщо один з намірів не може бути виконаний, вся операція скасовується з помилкою.

swift
let coordinator = NSFileCoordinator()
let readIntent = NSFileCoordinator
    .ReadingIntent(url: sourceURL)
let writeIntent = NSFileCoordinator
    .WritingIntent(url: destURL)

coordinator.coordinate(
    with: [readIntent, writeIntent]
) { error in
    try? FileManager.default
        .copyItem(at: sourceURL, to: destURL)
}

Асинхронна координація — починаючи з iOS 15, NSFileCoordinator підтримує асинхронні методи з completion handler, що дозволяє виконувати координацію без блокування викликаючого потоку. Це критично важливо для UI-потоку, де синхронне очікування координації може викликати зависання інтерфейсу на секунди.

Протокол NSFilePresenter та сповіщення

NSFilePresenter — це протокол, який об'єкт реалізує для отримання сповіщень про зміни файлів, що координуються NSFileCoordinator. Якщо ваш застосунок відображає вміст файлу, який може бути змінений іншим процесом (наприклад, iCloud Drive синхронізує нову версію), реалізація NSFilePresenter дозволяє своєчасно оновити інтерфейс.

swift
class DocumentPresenter: NSFilePresenter {
    let presentedItemURL: URL?
    let presentedItemOperationQueue: OperationQueue

    init(url: URL) {
        presentedItemURL = url
        presentedItemOperationQueue = OperationQueue()
    }

    func presentedItemDidChange() {
        DispatchQueue.main.async {
            NotificationCenter.default
                .post(name: .documentDidChange,
                      object: self)
        }
    }

    func presentedItemDidMove(to newURL: URL) {
        Logger.storage.info(
            "File moved to: \(newURL.lastPathComponent)"
        )
    }

    func accommodatePresentedItemDeletion(
        completionHandler: @escaping (Error?) -> Void
    ) {
        Logger.storage.warn("File deleted externally")
        completionHandler(nil)
    }
}

Методи протоколу: presentedItemDidChange викликається при зміні вмісту файлу, presentedItemDidMove(to:) — після переміщення файлу, accommodatePresentedItemDeletion — перед видаленням файлу іншим процесом (дозволяє застосунку коректно закрити файл). Додатково протокол підтримує версіонування через presentedItemDidGainVersion: та presentedItemDidLoseVersion:.

Важливо: NSFilePresenter повинен бути зареєстрований в системі через NSFileCoordinator.addFilePresenter:. Без реєстрації сповіщення не будуть доставлятися. Реєстрація виконується один раз при запуску застосунку і не потребує повторної реєстрації при перестворенні презентера.

Найкращі практики координації файлів

Завжди використовуйте координатор для файлів в Ubiquity container (iCloud Drive) та директоріях, доступних розширенням. Навіть якщо зараз застосунок однопотоковий, майбутні оновлення або системні зміни можуть додати паралельний доступ, і відсутність координації призведе до важковідловлюваних багів.

Мінімізуйте час в блоці координації. Поки блок виконується, інші процеси не можуть отримати доступ до файлу. Тривалі операції всередині блоку (складна обробка даних, мережеві запити) блокують всю систему файлового доступу. Виконуйте лише читання або запис даних всередині блоку, а обробку — за його межами.

Уникайте deadlock-ів: не викликайте координатор зсередини блоку іншого координатора для того ж файлу — це призведе до взаємного блокування. Використовуйте пакетні операції (масив намірів) замість вкладених викликів. Якщо вкладеність необхідна, використовуйте різні черги або різні URL.

За даними objc.io (2024), типові помилки при роботі з NSFileCoordinator включають: відсутність обробки помилок в completion handler (призводить до незавершених операцій); координація лише для запису, але не для читання; використання застарілого синхронного API в UI-потоці; ігнорування протоколу NSFilePresenter при роботі з iCloud Drive. Остання помилка — найпідступніша: застосунок показує застарілі дані, не підозрюючи, що файл вже змінено.

Часті запитання

Що таке NSFileCoordinator?

NSFileCoordinator — клас Foundation для безпечного доступу до файлів з кількох потоків або процесів. Він запобігає станам гонки, узгоджуючи операції читання та запису на рівні файлової системи.

Чим NSFileCoordinator відрізняється від NSLock?

NSLock працює лише всередині одного процесу (між потоками). NSFileCoordinator координує доступ між різними процесами та розширеннями, включаючи синхронізацію iCloud Drive та File Provider Extension.

Чи обов'язково використовувати NSFileCoordinator для iCloud Drive?

Так, Apple наполегливо рекомендує використовувати NSFileCoordinator для всіх операцій з файлами Ubiquity container. Без координатора можливі пошкодження даних при синхронізації між пристроями та конфлікти з File Provider Extension.

Що таке NSFilePresenter?

NSFilePresenter — протокол для отримання сповіщень про зміни файлів. Дозволяє застосунку реагувати на зміни, зроблені іншими процесами: оновлювати UI при модифікації, обробляти переміщення або підготуватися до видалення файлу.

Які типи намірів підтримує NSFileCoordinator?

П'ять типів: ReadingIntent (читання), WritingIntent (запис), ReadingIntent з .withoutChanges (читання без відстеження), WritingIntent з .contentIndependentMetadataOnly (лише метадані) та WritingIntent з .forDeleting (видалення). Кожен визначає рівень доступу до файлу.

Підсумки

  • NSFileCoordinator — системний механізм для безпечного доступу до файлів з потоків, процесів та розширень
  • Координаційні наміри (читання/запис) декларують тип операції до її виконання
  • Читання дозволено паралельно, запис — ексклюзивно (модель readers-writer)
  • NSFilePresenter — протокол сповіщень про зміни файлів з інших процесів
  • iCloud Drive та File Provider вимагають обов'язкової координації для запобігання пошкодженню даних
  • Мінімізуйте час в блоці координації — тривалі операції блокують доступ інших процесів
  • Deadlock-и запобігаються через пакетні операції та уникання вкладених викликів координатора

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також