Como faço para compartilhar um armazenamento de Core Data entre processos usando NSDistributedNotifications?
fundo
Eu já postei uma pergunta sobre o básico docompartilhando um armazenamento de dados do núcleo entre processos.
Estou tentando implementar as recomendações dadas e estou com problemas.
Meu gol
Eu tenho dois processos - o aplicativo auxiliar e a interface do usuário. Ambos compartilham um único armazenamento de dados. Eu quero que a interface do usuário para atualizar seu NSManagedObjectContext quando o aplicativo auxiliar salvou novos dados para o armazenamento.
Fluxo atual do programa
O processo do aplicativo auxiliar grava dados no armazenamento.
No aplicativo auxiliar, eu ouço notificações NSManagedObjectContextDidSaveNotification.
Quando o contexto é salvo, eu codifico os objetos inseridos, excluídos e atualizados usando suas representações de URI e NSArchiver.
Eu envio um NSNotification para o NSDistributedNotificationCenter com este dicionário codificado como o userInfo.
O processo da interface do usuário está escutando a notificação de salvamento. Quando recebe a notificação, desarquiva o userInfo usando o NSUnarchiver.
Ele procura todos os objetos atualizados / inseridos / excluídos dos URIs fornecidos e os substitui por NSManagedObjects.
Ele constrói uma NSNotification com os objetos atualizados / inseridos / excluídos.
Eu chamo mergeChangesFromContextDidSaveNotification: no contexto de objeto gerenciado do processo de interface do usuário, passando o NSNotification que eu construí na etapa anterior.
O problema
Objetos inseridos são corrigidos no contexto de objeto gerenciado da interface do usuário e aparecem na interface do usuário. O problema vem com objetos atualizados. Eles simplesmente não atualizam.
O que eu tentei
A coisa mais óbvia para tentar seria passar a notificação de salvamento do processo do aplicativo auxiliar para o processo da interface do usuário. Fácil, certo? Bem não. Notificações distribuídas não permitem que eu faça isso, pois o dicionário userInfo não está no formato correto. É por isso que estou fazendo todas as coisas do NSArchiving.
Eu tentei chamar refreshObject: mergeChanges: YES no NSManagedObjects para ser atualizado, mas isso não parece ter qualquer efeito.
Eu tentei executar o seletor mergeChangesFromContextDidSaveNotification: no thread principal e no thread atual. Nem parece afetar o resultado.
Eu tentei usar mergeChangesFromContextDidSaveNotification: antes entre os threads, o que obviamente é muito mais simples e funcionou perfeitamente. Mas preciso dessa mesma funcionalidade entre processos.
Alternativas?
Estou faltando alguma coisa aqui? Estou constantemente sentindo que estou tornando isso muito mais complexo do que precisa ser, mas depois de ler a documentação várias vezes e passar alguns dias inteiros nisso, não vejo outra maneira de atualizar o MOC de a interface do usuário.
Existe uma maneira mais elegante de fazer isso? Ou eu estou apenas cometendo um erro bobo em algum lugar no meu código?
O código
Eu tentei torná-lo o mais legível possível, mas ainda é uma bagunça. Desculpa.
Código de aplicativo auxiliar
-(void)workerThreadObjectContextDidSave:(NSNotification *)saveNotification {
NSMutableDictionary *savedObjectsEncodedURIs = [NSMutableDictionary dictionary];
NSArray *savedObjectKeys = [[saveNotification userInfo] allKeys];
for(NSString *thisSavedObjectKey in savedObjectKeys) {
// This is the set of updated/inserted/deleted NSManagedObjects.
NSSet *thisSavedObjectSet = [[saveNotification userInfo] objectForKey:thisSavedObjectKey];
NSMutableSet *thisSavedObjectSetEncoded = [NSMutableSet set];
for(id thisSavedObject in [thisSavedObjectSet allObjects]) {
// Construct a set of URIs that will be encoded as NSData
NSURL *thisSavedObjectURI = [[(NSManagedObject *)thisSavedObject objectID] URIRepresentation];
[thisSavedObjectSetEncoded addObject:thisSavedObjectURI];
}
// Archive the set of URIs.
[savedObjectsEncodedURIs setObject:[NSArchiver archivedDataWithRootObject:thisSavedObjectSetEncoded] forKey:thisSavedObjectKey];
}
if ([[savedObjectsEncodedURIs allValues] count] > 0) {
// Tell UI process there are new objects that need merging into it's MOC
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"com.synapticmishap.lapsus.save" object:@"HelperApp" userInfo:(NSDictionary *)savedObjectsEncodedURIs];
}
}
Código da interface do usuário
-(void)mergeSavesIntoMOC:(NSNotification *)notification {
NSDictionary *objectsToRefresh = [notification userInfo];
NSMutableDictionary *notificationUserInfo = [NSMutableDictionary dictionary];
NSArray *savedObjectKeys = [[notification userInfo] allKeys];
for(NSString *thisSavedObjectKey in savedObjectKeys) {
// Iterate through all the URIs in the decoded set. For each URI, get the NSManagedObject and add it to a set.
NSSet *thisSavedObjectSetDecoded = [NSUnarchiver unarchiveObjectWithData:[[notification userInfo] objectForKey:thisSavedObjectKey]];
NSMutableSet *savedManagedObjectSet = [NSMutableSet set];
for(NSURL *thisSavedObjectURI in thisSavedObjectSetDecoded) {
NSManagedObject *thisSavedManagedObject = [managedObjectContext objectWithID:[persistentStoreCoordinator managedObjectIDForURIRepresentation:thisSavedObjectURI]];
[savedManagedObjectSet addObject:thisSavedManagedObject];
// If the object is to be updated, refresh the object and merge in changes.
// This doesn't work!
if ([thisSavedObjectKey isEqualToString:NSUpdatedObjectsKey]) {
[managedObjectContext refreshObject:thisSavedManagedObject mergeChanges:YES];
[managedObjectContext save:nil];
}
}
[notificationUserInfo setObject:savedManagedObjectSet forKey:thisSavedObjectKey];
}
// Build a notification suitable for merging changes into MOC.
NSNotification *saveNotification = [NSNotification notificationWithName:@"" object:nil userInfo:(NSDictionary *)notificationUserInfo];
[managedObjectContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:saveNotification
waitUntilDone:YES];
}