Wie teile ich einen Core-Datenspeicher mit NSDistributedNotifications zwischen Prozessen?

Hintergrund

Ich habe bereits eine Frage zu den Grundlagen von gepostetGemeinsame Nutzung eines Core-Datenspeichers zwischen Prozessen.

Ich versuche, die gegebenen Empfehlungen umzusetzen und stoße auf Probleme.

Mein Ziel

Ich habe zwei Prozesse - die Helfer-App und die Benutzeroberfläche. Beide teilen sich einen einzigen Datenspeicher. Ich möchte, dass die Benutzeroberfläche ihren NSManagedObjectContext aktualisiert, wenn die Helper-App neue Daten im Store gespeichert hat.

Aktueller Programmablauf

Der Helper App-Prozess schreibt Daten in den Store.

In der Helper-App achte ich auf NSManagedObjectContextDidSaveNotification-Benachrichtigungen.

Wenn der Kontext gespeichert ist, codiere ich die eingefügten, gelöschten und aktualisierten Objekte mit ihren URI-Darstellungen und NSArchiver.

Ich sende eine NSNotification mit diesem codierten Wörterbuch als userInfo an das NSDistributedNotificationCenter.

Der Benutzeroberflächenprozess wartet auf die Speicherbenachrichtigung. Wenn die Benachrichtigung empfangen wird, wird die userInfo mithilfe von NSUnarchiver aus der Archivierung entfernt.

Es sucht alle aktualisierten / eingefügten / gelöschten Objekte in den angegebenen URIs und ersetzt sie durch NSManagedObjects.

Es erstellt eine NSNotification mit den aktualisierten / eingefügten / gelöschten Objekten.

Ich rufe mergeChangesFromContextDidSaveNotification auf: im verwalteten Objektkontext des UI-Prozesses, wobei ich die im vorherigen Schritt erstellte NSNotification übergebe.

Das Problem

Eingefügte Objekte sind fehlerhaft in der Benutzerschnittstelle für verwaltete Objektkontexte und werden in der Benutzerschnittstelle angezeigt. Das Problem tritt bei aktualisierten Objekten auf. Sie aktualisieren einfach nicht.

Was ich versucht habe

Am naheliegendsten wäre es, die Benachrichtigung über das Speichern vom Hilfsprogramm-Prozess an den Benutzeroberflächenprozess zu übergeben. Einfach richtig? Nun, nein. Verteilte Benachrichtigungen erlauben mir das nicht, da das userInfo-Wörterbuch nicht das richtige Format hat. Deshalb mache ich das ganze NSArchiving-Zeug.

Ich habe versucht, refreshObject: mergeChanges: YES für die zu aktualisierenden NSManagedObjects aufzurufen, aber dies scheint keine Auswirkungen zu haben.

Ich habe versucht, den mergeChangesFromContextDidSaveNotification: Selektor auf dem Hauptthread und dem aktuellen Thread durchzuführen. Beides scheint das Ergebnis nicht zu beeinflussen.

Ich habe versucht, mergeChangesFromContextDidSaveNotification zu verwenden: Vorher zwischen Threads, was natürlich viel einfacher ist und perfekt funktioniert. Aber ich brauche die gleiche Funktionalität zwischen den Prozessen.

Alternativen?

Vermisse ich hier etwas? Ich habe durchweg das Gefühl, dass ich das viel komplexer mache, als es sein muss, aber nachdem ich die Dokumentation mehrmals gelesen und ein paar gute Tage damit verbracht habe, sehe ich keine andere Möglichkeit, das MOC von zu aktualisieren die Benutzeroberfläche.

Gibt es eine elegantere Art, dies zu tun? Oder mache ich nur irgendwo in meinem Code einen dummen Fehler?

Der Code

Ich habe versucht, es so lesbar wie möglich zu machen, aber es ist immer noch ein Chaos. Es tut uns leid.

Helper-App-Code

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

UI-Code

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

Antworten auf die Frage(6)

Ihre Antwort auf die Frage