NSFileCoordinator é uma classe Foundation no iOS e macOS que garante acesso seguro a arquivos quando múltiplas threads, processos ou extensões trabalham simultaneamente. De acordo com a Documentação do Desenvolvedor Apple, 2024, NSFileCoordinator previne condições de corrida ao ler e escrever arquivos, garantindo que nenhum processo leia dados enquanto outro os está modificando. O coordenador é usado no iCloud Drive, File Provider Extension e em qualquer operação de arquivos multithread.
Principais conclusões
NSFileCoordinator é um mecanismo de sincronização de acesso a arquivos a nível do sistema operacional, apresentado pela Apple no iOS 5 e macOS 10.7 Lion. Ao contrário dos locks tradicionais (NSLock, pthread_mutex), o coordenador funciona a nível do sistema de arquivos e pode coordenar o acesso entre diferentes processos, não apenas entre threads da mesma aplicação.
A necessidade do NSFileCoordinator surge da arquitetura Sandbox no iOS: cada processo (aplicação, extensão, serviço do sistema) executa em um ambiente isolado com seu próprio acesso a arquivos. Quando múltiplos processos tentam ler e escrever o mesmo arquivo simultaneamente (por exemplo, durante a sincronização do iCloud Drive), sem um coordenador ocorrem condições de corrida: o processo A lê o arquivo enquanto o processo B já o sobrescreveu parcialmente.
De acordo com a WWDC 2023, a Apple recomenda fortemente o uso do NSFileCoordinator para todas as operações de arquivos no container Ubiquity (iCloud Drive) e ao trabalhar com File Provider Extension. Ignorar a coordenação é uma das causas comuns de corrupção de dados e bugs não reproduzíveis em aplicações iOS.
Intenção de coordenação (NSFileCoordinator.ReadingIntent / WritingIntent) é um objeto que declara o tipo de operação que uma thread ou processo planeja realizar. O coordenador usa essas intenções para determinar a ordem de acesso e resolver conflitos.
| Tipo de intenção | Descrição | Quando usar |
|---|---|---|
| ReadingIntent | Leitura de arquivo sem modificações | Abrir um documento, carregar dados |
| WritingIntent | Escrita com possível modificação de conteúdo | Salvar um documento, editar |
| ReadingIntent(URL, options: .withoutChanges) | Leitura sem rastreamento de alterações | Pré-visualização rápida de conteúdo |
| WritingIntent(URL, options: .contentIndependentMetadataOnly) | Modificação apenas de metadados | Atualizar data ou atributos |
| WritingIntent(URL, options: .forDeleting) | Exclusão de arquivo | Exclusão de documento pelo usuário |
Regras de coordenação: múltiplas leituras simultâneas são permitidas (se não houver escrita ativa), a escrita é exclusiva — nenhuma leitura ou escrita é permitida durante uma operação de escrita. Isso segue o modelo de lock leitores-escritores, mas com suporte adicional para coordenação entre processos através de launchd e XPC.
Nuance importante: NSFileCoordinator não impede o acesso a arquivos via NSData ou FileManager comuns — ele coordena apenas as operações que estão explicitamente envolvidas em blocos de coordenação. Se outra thread acessar o arquivo diretamente sem o coordenador, ocorrem exatamente as condições de corrida que o coordenador foi projetado para prevenir.
Padrão básico de uso do NSFileCoordinator consiste em três passos: criar uma instância do coordenador, declarar uma intenção (leitura ou escrita) e realizar a operação dentro de um bloco de coordenação. O coordenador garante que nenhum outro coordenador trabalhe simultaneamente com o mesmo arquivo.
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)"
)
}
}
Operação em lote — o coordenador pode lidar com múltiplos arquivos em uma única operação usando um conjunto de intenções. Isso é conveniente para mover, copiar ou excluir um conjunto de arquivos como uma transação única. Se uma das intenções não puder ser cumprida, toda a operação é cancelada com um erro.
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)
}
Coordenação assíncrona — a partir do iOS 15, o NSFileCoordinator suporta métodos assíncronos com um handler de conclusão, permitindo coordenação sem bloquear a thread chamadora. Isso é crítico para a thread da UI, onde a espera síncrona por coordenação pode causar congelamento da interface por segundos.
NSFilePresenter é um protocolo que um objeto implementa para receber notificações sobre alterações em arquivos coordenados pelo NSFileCoordinator. Se sua aplicação exibe o conteúdo de um arquivo que pode ser alterado por outro processo (por exemplo, iCloud Drive sincroniza uma nova versão), implementar NSFilePresenter permite atualizar a interface oportunamente.
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 do protocolo: presentedItemDidChange é chamado quando o conteúdo do arquivo muda, presentedItemDidMove(to:) — após a realocação do arquivo, accommodatePresentedItemDeletion — antes da exclusão do arquivo por outro processo (permite que a aplicação feche o arquivo graciosamente). Adicionalmente, o protocolo suporta versionamento através de presentedItemDidGainVersion: e presentedItemDidLoseVersion:.
Importante: NSFilePresenter deve ser registrado no sistema através de NSFileCoordinator.addFilePresenter:. Sem registro, as notificações não serão entregues. O registro é realizado uma vez na inicialização da aplicação e não requer novo registro quando o apresentador é recriado.
Sempre use o coordenador para arquivos no container Ubiquity (iCloud Drive) e diretórios acessíveis por extensões. Mesmo que a aplicação atualmente seja single-thread, futuras atualizações ou mudanças do sistema podem adicionar acesso paralelo, e a falta de coordenação levará a bugs difíceis de encontrar.
Minimize o tempo dentro do bloco de coordenação. Enquanto o bloco está executando, outros processos não podem acessar o arquivo. Operações longas dentro do bloco (processamento complexo de dados, requisições de rede) bloqueiam todo o sistema de acesso a arquivos. Realize apenas leitura ou escrita de dados dentro do bloco e processe fora dele.
Evite deadlocks: não chame o coordenador de dentro do bloco de outro coordenador para o mesmo arquivo — isso causará um deadlock mútuo. Use operações em lote (conjunto de intenções) em vez de chamadas aninhadas. Se o aninhamento for necessário, use filas diferentes ou URLs diferentes.
De acordo com objc.io (2024), erros típicos ao trabalhar com NSFileCoordinator incluem: falta de tratamento de erros no handler de conclusão (leva a operações incompletas); coordenação apenas para escritas mas não para leituras; uso da API síncrona obsoleta na thread da UI; ignorar o protocolo NSFilePresenter ao trabalhar com iCloud Drive. O último erro é o mais insidioso: a aplicação exibe dados desatualizados sem perceber que o arquivo já foi modificado.
Perguntas frequentes
NSFileCoordinator é uma classe Foundation para acesso seguro a arquivos de múltiplas threads ou processos. Ele previne condições de corrida coordenando operações de leitura e escrita a nível do sistema de arquivos.
NSLock funciona apenas dentro de um único processo (entre threads). NSFileCoordinator coordena o acesso entre diferentes processos e extensões, incluindo sincronização do iCloud Drive e File Provider Extension.
Sim, a Apple recomenda fortemente o uso do NSFileCoordinator para todas as operações de arquivos no container Ubiquity. Sem o coordenador, podem ocorrer corrupção de dados durante a sincronização entre dispositivos e conflitos com File Provider Extension.
NSFilePresenter é um protocolo para receber notificações sobre alterações em arquivos. Permite que a aplicação reaja a alterações feitas por outros processos: atualizar a UI em modificações, lidar com realocações ou preparar-se para exclusão de arquivos.
Cinco tipos: ReadingIntent (leitura), WritingIntent (escrita), ReadingIntent com .withoutChanges (leitura sem rastreamento), WritingIntent com .contentIndependentMetadataOnly (apenas metadados) e WritingIntent com .forDeleting (exclusão). Cada um define o nível de acesso ao arquivo.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também