Jak udostępnić magazyn danych podstawowych między procesami za pomocą NSDistributedNotifications?

tło

Wysłałem już pytanie o podstawyudostępnianie magazynu danych podstawowych między procesami.

Próbuję wdrożyć podane zalecenia i napotykam problemy.

Mój cel

Mam dwa procesy - aplikację pomocniczą i interfejs użytkownika. Oboje dzielą jeden magazyn danych. Chcę, aby interfejs użytkownika zaktualizował to NSManagedObjectContext, gdy aplikacja pomocnicza zapisała nowe dane w sklepie.

Przepływ bieżącego programu

Proces aplikacji pomocnika zapisuje dane w sklepie.

W aplikacji Helper słucham powiadomień NSManagedObjectContextDidSaveNotification.

Po zapisaniu kontekstu koduję wstawione, usunięte i zaktualizowane obiekty za pomocą ich reprezentacji URI i NSArchiver.

Wysyłam NSNotification do NSDistributedNotificationCenter z tym zakodowanym słownikiem jako userInfo.

Proces interfejsu użytkownika nasłuchuje powiadomienia o zapisaniu. Gdy otrzyma powiadomienie, archiwizuje userInfo za pomocą NSUnarchiver.

Sprawdza wszystkie zaktualizowane / wstawione / usunięte obiekty z podanych URI i zastępuje je obiektami NSManagedObject.

Tworzy NSNotification ze zaktualizowanymi / wstawionymi / usuniętymi obiektami.

Wywołuję mergeChangesFromContextDidSaveNotification: w kontekście zarządzanego obiektu procesu interfejsu użytkownika, przekazując skonstruowane w poprzednim kroku NSNotification.

Problem

Wstawione obiekty są błędnie wprowadzane do kontekstu zarządzanego obiektu interfejsu użytkownika i pojawiają się w interfejsie użytkownika. Problem pochodzi ze zaktualizowanymi obiektami. Po prostu się nie aktualizują.

Co próbowałem

Najbardziej oczywistą rzeczą do wypróbowania byłoby przekazanie powiadomienia o zapisaniu z procesu aplikacji pomocnika do procesu interfejsu użytkownika. Łatwo, prawda? Więc nie. Powiadomienia rozproszone nie pozwalają mi tego zrobić, ponieważ słownik informacji użytkownika nie jest w odpowiednim formacie. Dlatego robię wszystkie rzeczy NSArchivingu.

Próbowałem wywołać metodę refreshObject: mergeChanges: YES w NSManagedObjects, które mają zostać zaktualizowane, ale to nie wydaje się mieć żadnego efektu.

Próbowałem wykonać selektor mergeChangesFromContextDidSaveNotification: w głównym wątku i bieżącym wątku. Żadne z nich nie wpływa na wynik.

Próbowałem użyć mergeChangesFromContextDidSaveNotification: wcześniej między wątkami, co oczywiście jest znacznie prostsze i działało idealnie. Ale potrzebuję tej samej funkcjonalności między procesami.

Alternatywy?

Czy coś mi umyka? Konsekwentnie odczuwam, że robię to bardziej skomplikowane niż to konieczne, ale po kilkukrotnym przeczytaniu dokumentacji i spędzeniu kilku dni na tym, nie widzę żadnego innego sposobu na odświeżenie MOC interfejs użytkownika.

Czy jest to bardziej elegancki sposób? A może po prostu robię głupi błąd gdzieś w moim kodzie?

Kod

Próbowałem uczynić go tak czytelnym, jak to tylko możliwe, ale nadal jest bałaganem. Przepraszam.

Kod aplikacji pomocnika

   -(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];
        }
    }

Kod UI

-(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];
}

questionAnswers(6)

yourAnswerToTheQuestion