2012-08-09 6 views
5

Ich arbeite an einer App, wo ich eine Remote-Suche zu einem Server machen möchte. Ich möchte, dass RestKit die abgerufenen Daten in der Datenbank speichert. Ich führe zuerst eine lokale Suche durch (die zurzeit funktioniert), dann möchte ich die Fernsuche machen und dann eine Tabellenansicht mit den neuen Ergebnissen aktualisieren.iOS, Remote-Server-Suche mit RestKit

Ich habe zwei Probleme, 1. wie soll meine Zuordnung aussehen und 2. der JSON gibt ein Array mit zwei verschiedenen Arten von Objekten zurück.

Die URL sieht wie folgt aus:

search.json?search=[search string] 

Die JSON es sieht wie folgt zurückgibt:

[ 
    { 
    "event": { 
     "id": 2, 
     [...] 
    }, 
    { 
    "news": { 
     "id": 16, 
     [...] 
    } 

Wo Ereignis und Nachrichten sind zwei Arten von Objekten.

In meiner App habe ich drei Modelle, Post (abstrakte Entität und Superklasse) NewsPost (Unterklasse zu Post) und Event (Unterklasse zu Post).

sieht meine Zuordnungen wie folgt aus:

RKManagedObjectMapping* newsMapping = [RKManagedObjectMapping mappingForClass:[NewsPost class] inManagedObjectStore:objectManager.objectStore]; 
newsMapping.primaryKeyAttribute = @"newsId"; 
newsMapping.rootKeyPath = @"news"; 
[newsMapping mapKeyPath:@"id" toAttribute:@"newsId"]; 

RKManagedObjectMapping *eventMapping = [RKManagedObjectMapping mappingForClass:[CalendarEvent class] inManagedObjectStore:objectManager.objectStore]; 
eventMapping.primaryKeyAttribute = @"calendarId"; 
eventMapping.rootKeyPath = @"calendars"; 
[eventMapping mapKeyPath:@"id" toAttribute:@"calendarId"]; 

// These two works. 
[objectManager.mappingProvider setObjectMapping:newsMapping forResourcePathPattern:@"/package_components/1/news"]; 
[objectManager.mappingProvider setObjectMapping:eventMapping forResourcePathPattern:@"/package_components/1/calendars"]; 

// I don't know how these should look/work. 
// Since the search word can change 
[objectManager.mappingProvider setObjectMapping:eventMapping forResourcePathPattern:@"/package_components/1/search\\.json?search="]; 
[objectManager.mappingProvider setObjectMapping:newsMapping forResourcePathPattern:@"/package_components/1/search\\.json?search="]; 

Meine Suche Code wie folgt (lokale Suche works) aussieht:

- (void)setUpSearch 
{ 
    if (self.searchField.text != nil) { 

     [self.posts removeAllObjects]; 
     [self.events removeAllObjects]; 
     [self.news removeAllObjects]; 

     // Search predicates. 
     // Performs local search. 
     NSPredicate *contactNamePredicate = [NSPredicate predicateWithFormat:@"contactName contains[cd] %@", self.searchField.text]; 
     NSPredicate *contactDepartmentPredicate = [NSPredicate predicateWithFormat:@"contactDepartment contains[cd] %@", self.searchField.text]; 
     [...] 

     NSArray *predicatesArray = [NSArray arrayWithObjects:contactNamePredicate, contactDepartmentPredicate, contactEmailPredicate, contactPhonePredicate, linkPredicate, titlePredicate, nil]; 

     NSPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicatesArray]; 

     self.posts = [[Post findAllWithPredicate:predicate] mutableCopy]; 

     if (self.posts.count != 0) { 
      self.noResultsLabel.hidden = YES; 
      for (int i = 0; i < self.posts.count; i++) { 
       Post * post = [self.posts objectAtIndex:i]; 
       if (post.calendarEvent == YES) { 
        [self.events addObject:post]; 
       } else { 
        [self.news addObject:post]; 
       } 
      } 
     } 

     // reload the table view 
     [self.tableView reloadData]; 

     [self performRemoteSearch]; 
    } 
} 

- (void)search 
{  
    [self setUpSearch]; 
    [self hideKeyboard]; 
    [self performRemoteSearch]; 
} 


- (void)performRemoteSearch 
{ 
    // Should load the objects from JSON  
    // Note that the searchPath can vary depending on search text. 
    NSString *searchPath = [NSString stringWithFormat:@"/package_components/1/search.json?search=%@", self.searchField.text]; 
    RKObjectManager *objectManager = [RKObjectManager sharedManager]; 
    [objectManager loadObjectsAtResourcePath:searchPath delegate:self]; 
} 

- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects 
{ 
    // This never gets called. 

    // Should update my arrays and then update the tableview, but it never gets called. 
    // Instead I get Error Domain=org.restkit.RestKit.ErrorDomain Code=1001 "Could not find an object mapping for keyPath: '' 
} 

Irgendwelche Tipps, wie ich sollte oder tun würde sehr geschätzt werden könnte.

+0

Hallo, Ich denke, diese Frage zu versuchen. 1. Können Sie das Format von JSON in irgendeiner Weise ändern? Ich möchte wissen, ob Sie zu jedem der Ergebnisse einen Root-Key-Pfad hinzufügen können (das wird es erheblich vereinfachen). 2. Sie verwenden NSManagedObject für Nachrichten und Ereignisse. Möchten Sie das Mapping bei der Benutzersuche in Coredata speichern? –

+0

Hallo, ja, wenn nötig kann ich den JSON ändern. Und ja, ich möchte die Ergebnisse lokal zwischenspeichern und speichern, wenn die Benutzer suchen. – Anders

+0

Lassen Sie mich wissen, ob die Antwort für Sie funktioniert. –

Antwort

3

Ich habe noch nicht verwaltete Objekte verwendet, aber das erste, was Sie hier tun müssen, ist das Restkit-Protokoll über Objektzuordnung und Netzwerkanforderung zu aktivieren, damit Sie überprüfen können, was Restkit vom Server erhält und wie das Mapping funktioniert.

//This can be added in your app delegate 
RKLogConfigureByName("RestKit/Network", RKLogLevelDebug); 
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace); 

Auf dem zweiten Platz, nach Ihrer JSON und dass die Änderungen Suchpfad, denkt ich ist besser Mapping für Schlüsselpfad zu verwenden, anstatt Muster des Ressourcenpfades. So sollten Sie versuchen, mit Schlüssel auf der Karte, wie in diesem Beispiel:

RKObjectMapping* articleMapping = [RKObjectMapping mappingForClass:[Article class]]; 
[articleMapping mapKeyPath:@"title" toAttribute:@"title"]; 
[articleMapping mapKeyPath:@"body" toAttribute:@"body"]; 
[articleMapping mapKeyPath:@"author" toAttribute:@"author"]; 
[articleMapping mapKeyPath:@"publication_date" toAttribute:@"publicationDate"]; 

[[RKObjectManager sharedManager].mappingProvider setMapping:articleMapping forKeyPath:@"articles"]; 

Und dann Ihre Daten geladen werden wie:

- (void)loadArticles { 
    [[RKObjectManager sharedManager] loadObjectsAtResourcePath:@"/articles" delegate:self]; 
} 

Der andere Weg, dies zu tun, ist durch das Objekt auf der Karte, so RestKit erfaßt die Art von Objekt und führt die Zuordnung und Sie stellen die Anfrage an einen beliebigen Pfad.

Wenn Sie irgendeine Frage haben, lassen Sie bitte einen Kommentar und ich kann meine Antwort verbessern, wie gebraucht.

+0

Danke, ich werde es morgen testen. – Anders

1

Ich habe noch nie eine Frage mit einer Prämie zu beantworten versuchte, lassen Sie mich versuchen, eine nützliche Antwort von einer jüngeren Arbeit geben =)

1. Wie soll mein Mapping aussieht

Aus Dein Code, alles sieht gut aus. Gibt es Verschachtelung von Objekten? Müssen Sie für das Zurücksetzen auf den Server serialisieren?

2. Der JSON gibt ein Array mit zwei verschiedenen Arten von Objekten zurück.

Sind Ihre Attribute gleich (d. H.Ereignis hat einen Titel, Ereignis hat ein Datum) ohne Überraschungen? Wenn nicht, müssen Sie dynamic nesting verwenden. Wenn ein Ressourcenpfad (d. H. Ihr Suchpfad) eine Sammlung mit verschiedenen Objekten (Ihrem Fall) empfängt, müssen Sie dynamic object mapping verwenden, um die Objekte zu laden.

Da Sie die JSON-Struktur bearbeiten können, können die Dinge durch Nutzung von RestKit einfacher sein.

- Stellen Sie sicher, dass der JSON einen root_key_path für die zwei verschiedenen Objekttypen hat.

Aus einem alten Experiment und etwas googlen kann RestKit eine json-Ausgabe mit verschiedenen Objekten richtig zuordnen, wenn sie richtige rootKeyPaths haben. Resultierende JSON sollte eine grobe Struktur wie haben:

{ 
    "news" : [ 
    { 
     "id" : 1, 
     "title" : "Mohawk guy quits" 
    }, 
    { 
     "id" : 2, 
     "title" : "Obama gets mohawk" 
    } 
    ], 
    "events" : [ 
    { 
     "id" : 1, 
     "name" : "testing" 
    }, 
    { 
     "id" : 2, 
     "name" : "testing again" 
    } 
    ] 
} 

Ich kann nicht sicher sein, 100% das oben genannte ist richtig. Sie können experimentieren, indem Sie dafür sorgen, dass Ihre API nur Nachrichten zurückgibt. Wenn dies funktioniert, fügen Sie die Ereignisdaten in den Mix ein.

- Laden Sie die Objekte vom Server

// Make a NS dictionary and use stringByAppendingQueryParameters 

NSDictionary *searchParams = [NSDictionary dictionaryWithKeysAndObjects: 
           @"query",@"myQuery", 
           @"location",@"1.394168,103.895473", 
           nil]; 

[[RKObjectManager sharedManager] loadObjectsAtResourcePath:[@"/path/to/resource.json" stringByAppendingQueryParameters:searchParams] delegate:objectLoaderDelegate]; 

- Behandeln Sie das "echte" Suche in Ihrem Object Delegate

Wenn es funktioniert, sollten die Objekte in Ihrer Coredata Einheiten zugeordnet werden. Sie können eine lokale Suche mit der oben angegebenen Methode NSPredicate durchführen.

Ich bevorzuge das Design-Muster, wo RestKit loadObjects... verwendet, um Daten vom Server abzurufen und zu mappen, der Rest der Verarbeitung erfolgt lokal. Diese Entkopplung macht die Dinge "App-ähnlicher". Sie können andere Form der Manipulation mit NSPredicates machen.

- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects { 
    // Do some processing here on the array of returned objects or cede control to a method that 
    // you've built for the search, like the above method. 
} 

Ein Beispiel, wenn die Suche Use-Case-Restaurants in der Nähe ist, wird es wahrscheinlich sinnvoll, innerhalb der aktuellen lat/lon alle Restaurants zu laden, und dann die lokale Filterung nach Namen durchführen Coredata verwenden. Ihr Server wird Sie begeistern.

Lassen Sie es mich wissen und ich werde versuchen, die Antwort weiter zu verbessern.

+0

Danke für Ihre Antwort und Entschuldigung für meine verspätete Antwort. Ich habe es tatsächlich funktioniert, indem ich die Antwort von @clope mit einigen kleinen Änderungen verwendet habe. – Anders