NSFilePresenter — це протокол Foundation, який дозволяє об’єкту отримувати сповіщення про зміни файлів та каталогів у файловій системі iOS та macOS. Клас реалізує методи протоколу та реєструється через NSFileCoordinator, після чого система автоматично викликає ці методи під час будь-яких операцій з відстежуваним файлом. Згідно з документацією розробників Apple (2025), NSFilePresenter застосовується в додатках з багатопотоковим доступом до документів для запобігання конфліктів запису. Протокол обов’язково використовується в зв’язці з NSFileCoordinator — тільки так забезпечується безпечна координація доступу.
Головне
NSFilePresenter — це протокол Foundation, призначений для відстеження змін файлів та каталогів в операційних системах Apple. Протокол визначає набір методів, які об’єкт-спостерігач реалізує для отримання сповіщень про події файлової системи.
Основне завдання протоколу — забезпечити безпечний доступ до файлів у багатопотокових сценаріях. В iOS та macOS кілька процесів та потоків можуть одночасно звертатися до одного файлу через NSFileCoordinator, і NSFilePresenter гарантує, що кожен учасник отримає актуальний стан даних.
Протокол включено до Foundation починаючи з iOS 5.0 та macOS 10.7. Він застосовується в додатках, які працюють з документами, базами даних та будь-якими файлами, які можуть змінюватися одночасно з різних джерел — наприклад, під час синхронізації iCloud або спільного редагування.
Додатки орієнтовані на документи — основна область застосування NSFilePresenter. Додатки, які працюють з UIDocument або NSDocument, автоматично реєструють себе як презентерів через NSFileCoordinator. Це дозволяє коректно обробляти конфлікти при редагуванні одного файлу з кількох вікон або пристроїв.
Синхронізація iCloud — другий ключовий сценарій. Коли файл змінюється на одному пристрої, iCloud синхронізує його на всіх підключених пристроях. NSFilePresenter сповіщає додаток про ці зміни, дозволяючи своєчасно оновити інтерфейс.
Багатопотокові редактори — третій сценарій. У додатках, де фонові черги завантажують та зберігають дані одночасно з роботою користувача, NSFilePresenter запобігає станам гонки при запису та читанні файлів.
Механізм роботи NSFilePresenter базується на моделі делегування: об’єкт реалізує методи протоколу, реєструється через NSFileCoordinator і отримує виклики при кожній зміні відстежуваного файлу. Система сама визначає, коли відбулася зміна та які методи потрібно викликати.
Процес починається з того, що об’єкт створює екземпляр NSFileCoordinator та викликає метод координатора, передаючи URL файлу. Координатор перевіряє, чи є для цього URL зареєстровані презентери. Якщо так, він блокує доступ на читання або запис та сповіщає презентерам про майбутню зміну через методи протоколу.
Після завершення операції координатор знімає блокування та викликає фінальні сповіщення. Важливо, що презентер не керує потоком виконання — він лише реагує на події. За координацію повністю відповідає NSFileCoordinator.
Фаза підготовки — перед виконанням операції координатор викликає accommodatePresentedItemDeletion або accommodatePresentedSubitemDeletion. Презентер може обробити ситуацію або скасувати операцію, повернувши помилку. Ця фаза дозволяє додатку коректно завершити роботу з файлом перед його зміною.
Фаза сповіщення — після завершення операції координатор викликає presentedItemDidChange або presentedSubitemDidChange. Презентер отримує сигнал про те, що файл змінився, і може перечитати його вміст. Для переміщення файлу викликається presentedItemDidMoveToURL з новим розташуванням.
Фаза завершення — координатор знімає всі блокування та звільняє ресурси. Презентер може продовжувати роботу з оновленими даними. Усі три фази виконуються синхронно в одному потоці, тому методи протоколу повинні виконуватися швидко, без тривалих операцій вводу-виведення.
Протокол NSFilePresenter містить кілька обов’язкових та опційних методів. Єдина обов’язкова властивість — presentedItemURL, що повертає URL відстежуваного файлу або каталогу. Без цієї властивості об’єкт не може бути зареєстрований як презентер.
presentedItemURL — властивість типу URL?, яка повинна повертати шлях до відстежуваного файлу. Якщо об’єкт відстежує кілька файлів, властивість повертає URL основного елемента. Для каталогів повертається URL самого каталогу.
presentedItemDidChange — викликається після зміни вмісту відстежуваного файлу. У цьому методі презентер оновлює свій внутрішній стан та перезавантажує дані. Цей метод не отримує інформацію про те, що саме змінилося — лише факт зміни.
accommodatePresentedItemDeletion — викликається перед видаленням файлу. Презентер може зберегти поточний стан, закрити файлові дескриптори або скасувати операцію, повернувши NSError. Якщо метод повертає помилку, операція видалення не виконується.
presentedItemDidMoveToURL — викликається після переміщення або перейменування файлу. Метод отримує новий URL, і презентер повинен оновити посилання на файл. Без реалізації цього методу презентер продовжуватиме вказувати на старий, неіснуючий шлях.
NSFileCoordinator та NSFilePresenter — нерозривна пара. NSFileCoordinator керує доступом до файлів та викликає методи презентера. Презентер не працює безпосередньо з файловою системою — усі операції проходять через координатора, який гарантує атомарність змін.
Координатор реєструє презентера через метод addFilePresenter класу NSFileCoordinator. Після додавання презентер починає отримувати сповіщення. Видалення відбувається через removeFilePresenter. Система зберігає слабке посилання на презентера, тому об’єкт повинен бути живим протягом всього періоду відстеження.
За даними Apple WWDC 2022, NSFileCoordinator використовує механізм координації на рівні ядра, що забезпечує мінімальну затримку при блокуванні. У новітніших версіях iOS координатор оптимізований для роботи з Sandbox та розширеннями додатків.
Intention — кожна операція читання або запису повинна бути обгорнута в блок координації: читання через coordinateReadingItemAtURL, запис через coordinateWritingItemAtURL. Координатор автоматично блокує файл для інших учасників на час виконання блоку.
Batch coordination — для операцій з кількома файлами використовується пакетна координація. Координатор атомарно блокує всі зазначені файли, виконує операцію та знімає блокування. Це критично важливо при переміщенні або копіюванні наборів документів.
Створимо клас DocumentPresenter, який реалізує протокол NSFilePresenter та відстежує зміни файлу документа. Клас містить посилання на файл, внутрішні дані та прапорець актуальності.
import Foundation
class DocumentPresenter: NSObject, NSFilePresenter {
var presentedItemURL: URL? {
return self.fileURL
}
var presentedItemOperationQueue: OperationQueue {
return self.queue
}
private let fileURL: URL
private let queue = OperationQueue()
func presentedItemDidChange() {
self.reloadData()
}
func accommodatePresentedItemDeletion() throws {
try self.saveCurrentState()
}
private func reloadData() {
let coordinator = NSFileCoordinator(filePresenter: self)
var error: NSError?
coordinator.coordinate(readingItemAt: self.fileURL,
options: [],
error: &error)
{ readURL in
guard let data = try? Data(contentsOf: readURL)
else { return }
self.processData(data)
}
}
private func processData(_: Data) {
// Обробка даних документа
}
}
Клас реалізує presentedItemDidChange для перезавантаження даних при зміні файлу та accommodatePresentedItemDeletion для збереження стану перед видаленням. Черга операцій гарантує, що всі сповіщення обробляються послідовно.
Реєстрація презентера виконується через NSFileCoordinator.addFilePresenter при відкритті документа. Важливо передати координатору правильні опції читання — withoutChanges для операцій без модифікації або immediatelyAvailable для сценаріїв з негайним доступом.
Перша поширена помилка — відсутність реалізації presentedItemOperationQueue. Якщо не вказати чергу, сповіщення можуть надходити в довільному потоці, спричиняючи гонку даних. Завжди використовуйте послідовну OperationQueue для обробки сповіщень.
Друга помилка — блокування в методах презентера. Методи протоколу викликаються синхронно з координатора. Якщо презентер виконує тривалу операцію (запис у БД, мережевий запит), він блокує координатор для всіх інших учасників. Виносьте важкі операції в фонові черги.
Третя помилка — ігнорування accommodatePresentedItemDeletion. Якщо презентер не реалізує цей метод та не повертає помилку, файл може бути видалений без збереження поточного стану. Завжди зберігайте дані в цьому методі, якщо вони ще не записані на диск.
Четверта помилка — циклічна координація. Коли презентер всередині методу сповіщення знову викликає координатора для того ж файлу, виникає взаємне блокування. Перевіряйте прапорець isCoordinatedOperation перед початком координації всередині обробника.
| Помилка | Наслідок | Рішення |
|---|---|---|
| Немає черги операцій | Гонка даних у багатопотоці | Вказати OperationQueue |
| Блокування в методах | Зависання координатора | Винести в фоновий потік |
| Ігнорування видалення | Втрата даних при видаленні | Реалізувати збереження |
| Циклічна координація | Взаємне блокування додатка | Прапорець isCoordinatedOperation |
Часто запитувані питання
NSFileHandle — це низькорівнений інтерфейс для читання та запису даних, який не надає механізмів сповіщення про зміни від інших процесів. NSFilePresenter працює на рівні координації: він отримує події від системи при будь-якій зміні файлу, незалежно від джерела — іншого потоку, процесу або iCloud.
Так. NSFilePresenter не має сенсу без NSFileCoordinator. Презентер лише визначає методи-обробники, а координатор керує блокуваннями та викликає ці методи. Якщо ви використовуєте NSFilePresenter без координатора, сповіщення не будуть доставлятися.
Може, але з обмеженнями. Властивість presentedItemURL повертає лише один URL, тому для відстеження кількох файлів використовується протокол NSFilePresenter з додатковими методами для піделементів. Альтернатива — створити окремий екземпляр презентера для кожного файлу.
NSFilePresenter повністю сумісний з пісочницею iOS. Додаток може відстежувати файли лише всередині свого контейнера. Для доступу до файлів інших додатків використовуються App Groups або Security-Scoped Bookmarks. Координатор працює в межах дозволів пісочниці.
Використовуйте debounce або throttle всередині методу presentedItemDidChange. Створіть таймер з затримкою 0,3–0,5 секунди та скидайте його при кожному новому виклику. Після стабілізації виконайте перезавантаження даних. Це запобігає багаторазовій обробці одного пакета змін.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також