NSFileCoordinator — co to jest, koordynacja dostępu do plików iOS

Autor: IT Sectr Opublikowano: 2026-07-11 Czas czytania: 7 min

NSFileCoordinator to klasa Foundation w iOS i macOS, zapewniająca bezpieczny dostęp do plików przy jednoczesnej pracy wielu wątków, procesów lub rozszerzeń. Według Apple Developer Documentation, 2024, NSFileCoordinator zapobiega stanom wyścigu (race conditions) podczas odczytu i zapisu plików, gwarantując, że żaden proces nie odczyta danych w momencie ich zmiany przez inny. Koordynator jest używany w iCloud Drive, File Provider Extension i wszelkich wielowątkowych operacjach plikowych.

Najważniejsze

  • NSFileCoordinator — klasa do bezpiecznego dostępu do plików z wielu wątków i procesów
  • Bloki koordynacyjne (reading/writing intent) deklarują typ operacji przed jej wykonaniem
  • Zapobieganie race conditions — główne zadanie koordynatora przy równoległym dostępie
  • Wsparcie File Provider — koordynator jest obowiązkowy przy pracy z plikami iCloud Drive i rozszerzeń
  • NSFilePresenter — protokół do otrzymywania powiadomień o zmianach plików z innych procesów

Czym jest NSFileCoordinator?

NSFileCoordinator — to mechanizm synchronizacji dostępu do plików na poziomie systemu operacyjnego, wprowadzony przez Apple w iOS 5 i macOS 10.7 Lion. W przeciwieństwie do tradycyjnych blokad (NSLock, pthread_mutex), koordynator działa na poziomie systemu plików i może koordynować dostęp między różnymi procesami, a nie tylko między wątkami jednej aplikacji.

Konieczność stosowania NSFileCoordinator wynika z architektury Sandbox w iOS: każdy proces (aplikacja, rozszerzenie, serwis systemowy) działa w izolowanym środowisku z własnym dostępem do plików. Gdy wiele procesów próbuje jednocześnie czytać i zapisywać ten sam plik (na przykład przy synchronizacji iCloud Drive), bez koordynatora powstają stany wyścigu: proces A odczytuje plik w momencie, gdy proces B już częściowo go nadpisał.

Według WWDC 2023, Apple zdecydowanie zaleca używanie NSFileCoordinator dla wszystkich operacji na plikach w Ubiquity container (iCloud Drive) oraz przy pracy z File Provider Extension. Ignorowanie koordynacji — jedna z częstych przyczyn uszkodzenia danych i nieodtwarzalnych błędów w aplikacjach iOS.

Rodzaje intencji koordynacyjnych (intents)

Intencja koordynacyjna (NSFileCoordinator.ReadingIntent / WritingIntent) — to obiekt deklarujący typ operacji, którą planuje wykonać wątek lub proces. Koordynator używa tych intencji do określenia kolejności dostępu i rozwiązywania konfliktów.

Rodzaj intencjiOpisKiedy używać
ReadingIntentOdczyt pliku bez zmianOtwieranie dokumentu, ładowanie danych
WritingIntentZapis z możliwą zmianą zawartościZapisywanie dokumentu, edycja
ReadingIntent(URL, options: .withoutChanges)Odczyt bez śledzenia zmianSzybki podgląd zawartości
WritingIntent(URL, options: .contentIndependentMetadataOnly)Zmiana tylko metadanychAktualizacja daty lub atrybutów
WritingIntent(URL, options: .forDeleting)Usunięcie plikuUsuwanie dokumentu przez użytkownika

Zasady koordynacji: wiele równoczesnych odczytów jest dozwolonych (jeśli nie ma aktywnego zapisu), zapis jest ekskluzywny — żaden odczyt ani zapis nie są dozwolone podczas operacji zapisu. Odpowiada to modelowi readers-writer lock, ale z dodatkowym wsparciem koordynacji międzyprocesowej przez launchd i XPC.

Ważny niuans: NSFileCoordinator nie zapobiega dostępowi do pliku przez zwykłe NSData lub FileManager — koordynuje tylko te operacje, które są jawnie opakowane w bloki koordynacji. Jeśli inny wątek uzyskuje dostęp do pliku bezpośrednio, omijając koordynatora, powstają te same race conditions, które koordynator ma zapobiegać.

NSFileCoordinator w działaniu: przykłady kodu

Podstawowy wzorzec użycia NSFileCoordinator składa się z trzech kroków: utworzenie instancji koordynatora, zadeklarowanie intencji (odczyt lub zapis) i wykonanie operacji wewnątrz bloku koordynacji. Koordynator gwarantuje, że żaden inny koordynator nie będzie jednocześnie pracować z tym samym plikiem.

swift
import Foundation

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

// Bezpieczne odczytywanie
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)
}

// Bezpieczne zapisywanie
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)"
        )
    }
}

Operacja zbiorcza — koordynator może obsługiwać wiele plików w jednej operacji, używając tablicy intencji. Jest to wygodne do przenoszenia, kopiowania lub usuwania zestawu plików jako pojedynczej transakcji. Jeśli jedna z intencji nie może zostać wykonana, cała operacja jest anulowana z błędem.

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)
}

Koordynacja asynchroniczna — od iOS 15 NSFileCoordinator obsługuje metody asynchroniczne z completion handler, co pozwala na wykonanie koordynacji bez blokowania wątku wywołującego. Jest to krytycznie ważne dla wątku UI, gdzie synchroniczne oczekiwanie na koordynację może spowodować zawieszenie interfejsu na sekundy.

Protokół NSFilePresenter i powiadomienia

NSFilePresenter — to protokół, który obiekt implementuje w celu otrzymywania powiadomień o zmianach plików koordynowanych przez NSFileCoordinator. Jeśli twoja aplikacja wyświetla zawartość pliku, który może być zmieniony przez inny proces (na przykład iCloud Drive synchronizuje nową wersję), implementacja NSFilePresenter pozwala na bieżąco aktualizować interfejs.

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)
    }
}

Metody protokołu: presentedItemDidChange jest wywoływana przy zmianie zawartości pliku, presentedItemDidMove(to:) — po przeniesieniu pliku, accommodatePresentedItemDeletion — przed usunięciem pliku przez inny proces (pozwala aplikacji poprawnie zamknąć plik). Dodatkowo protokół obsługuje wersjonowanie przez presentedItemDidGainVersion: i presentedItemDidLoseVersion:.

Ważne: NSFilePresenter musi być zarejestrowany w systemie przez NSFileCoordinator.addFilePresenter:. Bez rejestracji powiadomienia nie będą dostarczane. Rejestracja jest wykonywana jednorazowo przy starcie aplikacji i nie wymaga ponownej rejestracji przy odtworzeniu prezentera.

Najlepsze praktyki koordynacji plików

Zawsze używaj koordynatora dla plików w Ubiquity container (iCloud Drive) i katalogach dostępnych dla rozszerzeń. Nawet jeśli obecnie aplikacja jest jednowątkowa, przyszłe aktualizacje lub zmiany systemowe mogą dodać równoległy dostęp, a brak koordynacji doprowadzi do trudnych do wychwycenia błędów.

Minimalizuj czas w bloku koordynacji. Podczas wykonywania bloku inne procesy nie mogą uzyskać dostępu do pliku. Długotrwałe operacje wewnątrz bloku (złożone przetwarzanie danych, żądania sieciowe) blokują cały system dostępu do plików. Wykonuj tylko odczyt lub zapis danych wewnątrz bloku, a przetwarzanie — poza nim.

Unikaj deadlock-ów: nie wywołuj koordynatora z wnętrza bloku innego koordynatora dla tego samego pliku — doprowadzi to do wzajemnej blokady. Używaj operacji zbiorczych (tablica intencji) zamiast zagnieżdżonych wywołań. Jeśli zagnieżdżenie jest konieczne, używaj różnych kolejek lub różnych URL-i.

Według objc.io (2024), typowe błędy przy pracy z NSFileCoordinator obejmują: brak obsługi błędów w completion handler (prowadzi do nieukończonych operacji); koordynacja tylko dla zapisu, ale nie dla odczytu; używanie przestarzałego synchronicznego API w wątku UI; ignorowanie protokołu NSFilePresenter przy pracy z iCloud Drive. Ostatni błąd jest najpodstępniejszy: aplikacja pokazuje nieaktualne dane, nie wiedząc, że plik został już zmieniony.

Często zadawane pytania

Czym jest NSFileCoordinator?

NSFileCoordinator — klasa Foundation do bezpiecznego dostępu do plików z wielu wątków lub procesów. Zapobiega race conditions, koordynując operacje odczytu i zapisu na poziomie systemu plików.

Czym NSFileCoordinator różni się od NSLock?

NSLock działa tylko wewnątrz jednego procesu (między wątkami). NSFileCoordinator koordynuje dostęp między różnymi procesami i rozszerzeniami, w tym synchronizację iCloud Drive i File Provider Extension.

Czy używanie NSFileCoordinator dla iCloud Drive jest obowiązkowe?

Tak, Apple zdecydowanie zaleca używanie NSFileCoordinator dla wszystkich operacji na plikach Ubiquity container. Bez koordynatora możliwe jest uszkodzenie danych podczas synchronizacji między urządzeniami i konflikty z File Provider Extension.

Czym jest NSFilePresenter?

NSFilePresenter — protokół do otrzymywania powiadomień o zmianach plików. Pozwala aplikacji reagować na zmiany dokonane przez inne procesy: aktualizować UI przy modyfikacji, obsługiwać przeniesienie lub przygotować się do usunięcia pliku.

Jakie typy intencji obsługuje NSFileCoordinator?

Pięć typów: ReadingIntent (odczyt), WritingIntent (zapis), ReadingIntent z .withoutChanges (odczyt bez śledzenia), WritingIntent z .contentIndependentMetadataOnly (tylko metadane) i WritingIntent z .forDeleting (usunięcie). Każdy określa poziom dostępu do pliku.

Podsumowanie

  • NSFileCoordinator — systemowy mechanizm bezpiecznego dostępu do plików z wątków, procesów i rozszerzeń
  • Intencje koordynacyjne (odczyt/zapis) deklarują typ operacji przed jej wykonaniem
  • Odczyt dozwolony równolegle, zapis — ekskluzywnie (model readers-writer)
  • NSFilePresenter — protokół powiadomień o zmianach plików z innych procesów
  • iCloud Drive i File Provider wymagają obowiązkowej koordynacji, aby zapobiec uszkodzeniu danych
  • Minimalizuj czas w bloku koordynacji — długotrwałe operacje blokują dostęp innych procesów
  • Deadlock-i są zapobiegane przez operacje zbiorcze i unikanie zagnieżdżonych wywołań koordynatora

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również