NSFileCoordinator is a Foundation class in iOS and macOS that ensures safe file access when multiple threads, processes, or extensions work simultaneously. According to Apple Developer Documentation, 2024, NSFileCoordinator prevents race conditions when reading and writing files, guaranteeing that no process reads data while another is modifying it. The coordinator is used in iCloud Drive, File Provider Extension, and any multithreaded file operations.
Key Takeaways
NSFileCoordinator is a file access synchronization mechanism at the operating system level, introduced by Apple in iOS 5 and macOS 10.7 Lion. Unlike traditional locks (NSLock, pthread_mutex), the coordinator works at the file system level and can coordinate access between different processes, not just between threads of the same application.
The need for NSFileCoordinator arises from the Sandbox architecture in iOS: each process (application, extension, system service) runs in an isolated environment with its own file access. When multiple processes try to read and write the same file simultaneously (e.g., during iCloud Drive synchronization), without a coordinator, race conditions occur: process A reads the file while process B has already partially overwritten it.
According to WWDC 2023, Apple strongly recommends using NSFileCoordinator for all file operations in the Ubiquity container (iCloud Drive) and when working with File Provider Extension. Ignoring coordination is one of the common causes of data corruption and non-reproducible bugs in iOS applications.
Coordination Intent (NSFileCoordinator.ReadingIntent / WritingIntent) is an object that declares the type of operation a thread or process plans to perform. The coordinator uses these intents to determine access order and resolve conflicts.
| Intent Type | Description | When to Use |
|---|---|---|
| ReadingIntent | Reading a file without modifications | Opening a document, loading data |
| WritingIntent | Writing with possible content modification | Saving a document, editing |
| ReadingIntent(URL, options: .withoutChanges) | Reading without change tracking | Quick content preview |
| WritingIntent(URL, options: .contentIndependentMetadataOnly) | Modifying metadata only | Updating date or attributes |
| WritingIntent(URL, options: .forDeleting) | Deleting a file | User document deletion |
Coordination rules: multiple simultaneous reads are allowed (if there is no active write), writing is exclusive — no reads or writes are allowed during a write operation. This follows the readers-writer lock model, but with additional support for inter-process coordination through launchd and XPC.
Important nuance: NSFileCoordinator does not prevent file access via regular NSData or FileManager — it only coordinates operations that are explicitly wrapped in coordination blocks. If another thread accesses the file directly without the coordinator, the very race conditions that the coordinator is designed to prevent will occur.
Basic pattern for using NSFileCoordinator consists of three steps: create a coordinator instance, declare an intent (read or write), and perform the operation inside a coordination block. The coordinator guarantees that no other coordinator will simultaneously work with the same file.
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)"
)
}
}
Batch operation — the coordinator can handle multiple files in one operation using an array of intents. This is convenient for moving, copying, or deleting a set of files as a single transaction. If one of the intents cannot be fulfilled, the entire operation is cancelled with an 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)
}
Asynchronous coordination — starting from iOS 15, NSFileCoordinator supports asynchronous methods with a completion handler, allowing coordination without blocking the calling thread. This is critical for the UI thread, where synchronous coordination waiting can cause interface freezing for seconds.
NSFilePresenter is a protocol that an object implements to receive notifications about changes to files coordinated by NSFileCoordinator. If your application displays the contents of a file that may be changed by another process (e.g., iCloud Drive syncs a new version), implementing NSFilePresenter allows you to update the interface in a timely manner.
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)
}
}
Protocol methods: presentedItemDidChange is called when file content changes, presentedItemDidMove(to:) — after file relocation, accommodatePresentedItemDeletion — before file deletion by another process (allows the application to close the file gracefully). Additionally, the protocol supports versioning through presentedItemDidGainVersion: and presentedItemDidLoseVersion:.
Important: NSFilePresenter must be registered in the system via NSFileCoordinator.addFilePresenter:. Without registration, notifications will not be delivered. Registration is performed once at application startup and does not require re-registration when the presenter is recreated.
Always use the coordinator for files in the Ubiquity container (iCloud Drive) and directories accessible to extensions. Even if the application is currently single-threaded, future updates or system changes may add parallel access, and the lack of coordination will lead to hard-to-find bugs.
Minimize time inside the coordination block. While the block is executing, other processes cannot access the file. Long operations inside the block (complex data processing, network requests) block the entire file access system. Perform only reading or writing of data inside the block, and handle processing outside of it.
Avoid deadlocks: do not call the coordinator from inside another coordinator's block for the same file — this will cause a mutual deadlock. Use batch operations (array of intents) instead of nested calls. If nesting is necessary, use different queues or different URLs.
According to objc.io (2024), typical errors when working with NSFileCoordinator include: missing error handling in the completion handler (leads to incomplete operations); coordinating only for writes but not for reads; using the outdated synchronous API on the UI thread; ignoring the NSFilePresenter protocol when working with iCloud Drive. The last error is the most insidious: the application displays outdated data without realizing the file has already been modified.
Frequently Asked Questions
NSFileCoordinator is a Foundation class for safe file access from multiple threads or processes. It prevents race conditions by coordinating read and write operations at the file system level.
NSLock works only within a single process (between threads). NSFileCoordinator coordinates access between different processes and extensions, including iCloud Drive synchronization and File Provider Extension.
Yes, Apple strongly recommends using NSFileCoordinator for all file operations in the Ubiquity container. Without the coordinator, data corruption can occur during synchronization between devices and conflicts with File Provider Extension.
NSFilePresenter is a protocol for receiving notifications about file changes. It allows the application to react to changes made by other processes: update the UI on modification, handle relocation, or prepare for file deletion.
Five types: ReadingIntent (read), WritingIntent (write), ReadingIntent with .withoutChanges (read without tracking), WritingIntent with .contentIndependentMetadataOnly (metadata only), and WritingIntent with .forDeleting (deletion). Each defines the level of file access.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also