NSFileCoordinator est une classe Foundation sur iOS et macOS qui garantit un accès sécurisé aux fichiers lorsque plusieurs threads, processus ou extensions travaillent simultanément. Selon la Documentation Développeur Apple, 2024, NSFileCoordinator empêche les conditions de course lors de la lecture et de l'écriture de fichiers, garantissant qu'aucun processus ne lit des données pendant qu'un autre les modifie. Le coordinateur est utilisé dans iCloud Drive, File Provider Extension et toute opération de fichiers multithread.
Points essentiels
NSFileCoordinator est un mécanisme de synchronisation d'accès aux fichiers au niveau du système d'exploitation, introduit par Apple dans iOS 5 et macOS 10.7 Lion. Contrairement aux verrous traditionnels (NSLock, pthread_mutex), le coordinateur fonctionne au niveau du système de fichiers et peut coordonner l'accès entre différents processus, pas seulement entre les threads d'une même application.
Le besoin de NSFileCoordinator découle de l'architecture Sandbox sur iOS : chaque processus (application, extension, service système) s'exécute dans un environnement isolé avec son propre accès aux fichiers. Lorsque plusieurs processus tentent de lire et d'écrire le même fichier simultanément (par exemple, lors de la synchronisation iCloud Drive), sans coordinateur, des conditions de course se produisent : le processus A lit le fichier tandis que le processus B l'a déjà partiellement écrasé.
Selon la WWDC 2023, Apple recommande vivement d'utiliser NSFileCoordinator pour toutes les opérations de fichiers dans le conteneur Ubiquity (iCloud Drive) et lors du travail avec File Provider Extension. Ignorer la coordination est l'une des causes courantes de corruption de données et de bugs non reproductibles dans les applications iOS.
Intention de coordination (NSFileCoordinator.ReadingIntent / WritingIntent) est un objet qui déclare le type d'opération qu'un thread ou un processus prévoit d'effectuer. Le coordinateur utilise ces intentions pour déterminer l'ordre d'accès et résoudre les conflits.
| Type d'intention | Description | Quand l'utiliser |
|---|---|---|
| ReadingIntent | Lecture de fichier sans modifications | Ouvrir un document, charger des données |
| WritingIntent | Écriture avec modification possible du contenu | Enregistrer un document, éditer |
| ReadingIntent(URL, options: .withoutChanges) | Lecture sans suivi des modifications | Aperçu rapide du contenu |
| WritingIntent(URL, options: .contentIndependentMetadataOnly) | Modification des métadonnées uniquement | Mise à jour de la date ou des attributs |
| WritingIntent(URL, options: .forDeleting) | Suppression de fichier | Suppression de document par l'utilisateur |
Règles de coordination : plusieurs lectures simultanées sont autorisées (s'il n'y a pas d'écriture active), l'écriture est exclusive — aucune lecture ou écriture n'est autorisée pendant une opération d'écriture. Cela suit le modèle de verrouillage lecteurs-écrivains, mais avec un support supplémentaire pour la coordination inter-processus via launchd et XPC.
Nuance importante : NSFileCoordinator n'empêche pas l'accès aux fichiers via NSData ou FileManager classiques — il coordonne uniquement les opérations qui sont explicitement encapsulées dans des blocs de coordination. Si un autre thread accède directement au fichier sans le coordinateur, les mêmes conditions de course que le coordinateur est conçu pour prévenir se produisent.
Modèle de base d'utilisation de NSFileCoordinator comprend trois étapes : créer une instance du coordinateur, déclarer une intention (lecture ou écriture) et effectuer l'opération à l'intérieur d'un bloc de coordination. Le coordinateur garantit qu'aucun autre coordinateur ne travaillera simultanément avec le même fichier.
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)"
)
}
}
Opération par lots — le coordinateur peut gérer plusieurs fichiers en une seule opération en utilisant un tableau d'intentions. C'est pratique pour déplacer, copier ou supprimer un ensemble de fichiers en une seule transaction. Si l'une des intentions ne peut pas être satisfaite, l'ensemble de l'opération est annulé avec une erreur.
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)
}
Coordination asynchrone — à partir d'iOS 15, NSFileCoordinator prend en charge les méthodes asynchrones avec un gestionnaire d'achèvement, permettant la coordination sans bloquer le thread appelant. C'est crucial pour le thread UI, où l'attente synchrone de coordination peut geler l'interface pendant des secondes.
NSFilePresenter est un protocole qu'un objet implémente pour recevoir des notifications sur les modifications des fichiers coordonnés par NSFileCoordinator. Si votre application affiche le contenu d'un fichier qui peut être modifié par un autre processus (par exemple, iCloud Drive synchronise une nouvelle version), implémenter NSFilePresenter permet de mettre à jour l'interface en temps utile.
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)
}
}
Méthodes du protocole : presentedItemDidChange est appelé lorsque le contenu du fichier change, presentedItemDidMove(to:) — après le déplacement du fichier, accommodatePresentedItemDeletion — avant la suppression du fichier par un autre processus (permet à l'application de fermer le fichier proprement). De plus, le protocole prend en charge le versionnage via presentedItemDidGainVersion: et presentedItemDidLoseVersion:.
Important : NSFilePresenter doit être enregistré dans le système via NSFileCoordinator.addFilePresenter:. Sans enregistrement, les notifications ne seront pas délivrées. L'enregistrement est effectué une fois au démarrage de l'application et ne nécessite pas de réenregistrement lors de la recréation du présentateur.
Utilisez toujours le coordinateur pour les fichiers dans le conteneur Ubiquity (iCloud Drive) et les répertoires accessibles aux extensions. Même si l'application est actuellement monothread, des mises à jour futures ou des changements système peuvent ajouter un accès parallèle, et l'absence de coordination entraînera des bugs difficiles à trouver.
Minimisez le temps dans le bloc de coordination. Pendant l'exécution du bloc, les autres processus ne peuvent pas accéder au fichier. Les opérations longues à l'intérieur du bloc (traitement complexe de données, requêtes réseau) bloquent tout le système d'accès aux fichiers. Effectuez uniquement la lecture ou l'écriture de données à l'intérieur du bloc et le traitement en dehors.
Évitez les interblocages : n'appelez pas le coordinateur depuis l'intérieur du bloc d'un autre coordinateur pour le même fichier — cela provoquera un interblocage mutuel. Utilisez des opérations par lots (tableau d'intentions) au lieu d'appels imbriqués. Si l'imbrication est nécessaire, utilisez des files d'attente différentes ou des URL différentes.
Selon objc.io (2024), les erreurs typiques lors du travail avec NSFileCoordinator incluent : l'absence de gestion des erreurs dans le gestionnaire d'achèvement (conduit à des opérations incomplètes) ; la coordination uniquement pour les écritures mais pas pour les lectures ; l'utilisation de l'API synchrone obsolète sur le thread UI ; l'ignorance du protocole NSFilePresenter lors du travail avec iCloud Drive. La dernière erreur est la plus insidieuse : l'application affiche des données obsolètes sans se rendre compte que le fichier a déjà été modifié.
Foire aux questions
NSFileCoordinator est une classe Foundation pour l'accès sécurisé aux fichiers depuis plusieurs threads ou processus. Il empêche les conditions de course en coordonnant les opérations de lecture et d'écriture au niveau du système de fichiers.
NSLock fonctionne uniquement à l'intérieur d'un seul processus (entre threads). NSFileCoordinator coordonne l'accès entre différents processus et extensions, y compris la synchronisation iCloud Drive et File Provider Extension.
Oui, Apple recommande vivement d'utiliser NSFileCoordinator pour toutes les opérations de fichiers dans le conteneur Ubiquity. Sans coordinateur, une corruption de données peut survenir lors de la synchronisation entre appareils et des conflits avec File Provider Extension.
NSFilePresenter est un protocole pour recevoir des notifications de modifications de fichiers. Il permet à l'application de réagir aux changements effectués par d'autres processus : mettre à jour l'UI lors de modifications, gérer les déplacements ou se préparer à la suppression de fichiers.
Cinq types : ReadingIntent (lecture), WritingIntent (écriture), ReadingIntent avec .withoutChanges (lecture sans suivi), WritingIntent avec .contentIndependentMetadataOnly (métadonnées uniquement) et WritingIntent avec .forDeleting (suppression). Chacun définit le niveau d'accès au fichier.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi