NSFileCoordinator es una clase de Foundation en iOS y macOS que garantiza el acceso seguro a archivos cuando varios hilos, procesos o extensiones trabajan simultáneamente. Según la Documentación para Desarrolladores de Apple, 2024, NSFileCoordinator previene condiciones de carrera al leer y escribir archivos, garantizando que ningún proceso lea datos mientras otro los está modificando. El coordinador se utiliza en iCloud Drive, File Provider Extension y cualquier operación de archivos multiproceso.
Puntos clave
NSFileCoordinator es un mecanismo de sincronización de acceso a archivos a nivel del sistema operativo, presentado por Apple en iOS 5 y macOS 10.7 Lion. A diferencia de los bloqueos tradicionales (NSLock, pthread_mutex), el coordinador funciona a nivel del sistema de archivos y puede coordinar el acceso entre diferentes procesos, no solo entre hilos de la misma aplicación.
La necesidad de NSFileCoordinator surge de la arquitectura Sandbox en iOS: cada proceso (aplicación, extensión, servicio del sistema) se ejecuta en un entorno aislado con su propio acceso a archivos. Cuando varios procesos intentan leer y escribir el mismo archivo simultáneamente (por ejemplo, durante la sincronización de iCloud Drive), sin un coordinador se producen condiciones de carrera: el proceso A lee el archivo mientras el proceso B ya lo ha sobrescrito parcialmente.
Según WWDC 2023, Apple recomienda firmemente usar NSFileCoordinator para todas las operaciones de archivos en el contenedor Ubiquity (iCloud Drive) y al trabajar con File Provider Extension. Ignorar la coordinación es una de las causas comunes de corrupción de datos y errores no reproducibles en aplicaciones iOS.
Intención de coordinación (NSFileCoordinator.ReadingIntent / WritingIntent) es un objeto que declara el tipo de operación que un hilo o proceso planea realizar. El coordinador utiliza estas intenciones para determinar el orden de acceso y resolver conflictos.
| Tipo de intención | Descripción | Cuándo usarlo |
|---|---|---|
| ReadingIntent | Lectura de archivo sin modificaciones | Abrir un documento, cargar datos |
| WritingIntent | Escritura con posible modificación del contenido | Guardar un documento, editar |
| ReadingIntent(URL, options: .withoutChanges) | Lectura sin seguimiento de cambios | Vista previa rápida de contenido |
| WritingIntent(URL, options: .contentIndependentMetadataOnly) | Modificación solo de metadatos | Actualizar fecha o atributos |
| WritingIntent(URL, options: .forDeleting) | Eliminación de archivo | Eliminación de documento por el usuario |
Reglas de coordinación: se permiten múltiples lecturas simultáneas (si no hay escritura activa), la escritura es exclusiva — no se permiten lecturas ni escrituras durante una operación de escritura. Esto sigue el modelo de bloqueo de lectores-escritores, pero con soporte adicional para coordinación entre procesos a través de launchd y XPC.
Matiz importante: NSFileCoordinator no impide el acceso a archivos mediante NSData o FileManager regulares — solo coordina las operaciones que están explícitamente envueltas en bloques de coordinación. Si otro hilo accede al archivo directamente sin el coordinador, se producen las mismas condiciones de carrera que el coordinador está diseñado para prevenir.
Patrón básico de uso de NSFileCoordinator consta de tres pasos: crear una instancia del coordinador, declarar una intención (lectura o escritura) y realizar la operación dentro de un bloque de coordinación. El coordinador garantiza que ningún otro coordinador trabaje simultáneamente con el mismo archivo.
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)"
)
}
}
Operación por lotes — el coordinador puede manejar múltiples archivos en una sola operación usando un conjunto de intenciones. Esto es conveniente para mover, copiar o eliminar un conjunto de archivos como una transacción única. Si una de las intenciones no puede cumplirse, toda la operación se cancela con un error.
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)
}
Coordinación asíncrona — a partir de iOS 15, NSFileCoordinator admite métodos asíncronos con un controlador de finalización, lo que permite la coordinación sin bloquear el hilo de llamada. Esto es crítico para el hilo de la UI, donde la espera sincrónica de coordinación puede provocar congelación de la interfaz durante segundos.
NSFilePresenter es un protocolo que un objeto implementa para recibir notificaciones sobre cambios en archivos coordinados por NSFileCoordinator. Si tu aplicación muestra el contenido de un archivo que puede ser modificado por otro proceso (por ejemplo, iCloud Drive sincroniza una nueva versión), implementar NSFilePresenter permite actualizar la interfaz de manera oportuna.
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étodos del protocolo: presentedItemDidChange se llama cuando cambia el contenido del archivo, presentedItemDidMove(to:) — después de la reubicación del archivo, accommodatePresentedItemDeletion — antes de la eliminación del archivo por otro proceso (permite a la aplicación cerrar el archivo correctamente). Adicionalmente, el protocolo admite versionado a través de presentedItemDidGainVersion: y presentedItemDidLoseVersion:.
Importante: NSFilePresenter debe estar registrado en el sistema a través de NSFileCoordinator.addFilePresenter:. Sin registro, las notificaciones no se entregarán. El registro se realiza una vez al iniciar la aplicación y no requiere nuevo registro cuando se recrea el presentador.
Usa siempre el coordinador para archivos en el contenedor Ubiquity (iCloud Drive) y directorios accesibles por extensiones. Incluso si la aplicación actualmente es de un solo hilo, futuras actualizaciones o cambios del sistema pueden agregar acceso paralelo, y la falta de coordinación provocará errores difíciles de encontrar.
Minimiza el tiempo dentro del bloque de coordinación. Mientras el bloque se ejecuta, otros procesos no pueden acceder al archivo. Las operaciones largas dentro del bloque (procesamiento complejo de datos, solicitudes de red) bloquean todo el sistema de acceso a archivos. Realiza solo lectura o escritura de datos dentro del bloque y maneja el procesamiento fuera de él.
Evita deadlocks: no llames al coordinador desde dentro del bloque de otro coordinador para el mismo archivo — esto causará un interbloqueo mutuo. Usa operaciones por lotes (conjunto de intenciones) en lugar de llamadas anidadas. Si el anidamiento es necesario, usa diferentes colas o diferentes URL.
Según objc.io (2024), los errores típicos al trabajar con NSFileCoordinator incluyen: falta de manejo de errores en el controlador de finalización (conduce a operaciones incompletas); coordinación solo para escrituras pero no para lecturas; uso de la API síncrona obsoleta en el hilo de la UI; ignorar el protocolo NSFilePresenter al trabajar con iCloud Drive. El último error es el más insidioso: la aplicación muestra datos desactualizados sin darse cuenta de que el archivo ya ha sido modificado.
Preguntas frecuentes
NSFileCoordinator es una clase de Foundation para el acceso seguro a archivos desde múltiples hilos o procesos. Previene condiciones de carrera coordinando operaciones de lectura y escritura a nivel del sistema de archivos.
NSLock funciona solo dentro de un único proceso (entre hilos). NSFileCoordinator coordina el acceso entre diferentes procesos y extensiones, incluyendo la sincronización de iCloud Drive y File Provider Extension.
Sí, Apple recomienda firmemente usar NSFileCoordinator para todas las operaciones de archivos en el contenedor Ubiquity. Sin el coordinador, pueden ocurrir daños en los datos durante la sincronización entre dispositivos y conflictos con File Provider Extension.
NSFilePresenter es un protocolo para recibir notificaciones sobre cambios en archivos. Permite a la aplicación reaccionar a cambios realizados por otros procesos: actualizar la UI ante modificaciones, manejar reubicaciones o prepararse para la eliminación de archivos.
Cinco tipos: ReadingIntent (lectura), WritingIntent (escritura), ReadingIntent con .withoutChanges (lectura sin seguimiento), WritingIntent con .contentIndependentMetadataOnly (solo metadatos) y WritingIntent con .forDeleting (eliminación). Cada uno define el nivel de acceso al archivo.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también