2012-08-09 16 views
5

Estoy trabajando en una aplicación en la que deseo realizar una búsqueda remota en un servidor. Quiero que RestKit guarde los datos recuperados en la base de datos. Primero realizo una búsqueda local (que actualmente funciona) luego deseo hacer la búsqueda remota y luego actualizar una vista de tabla con los nuevos resultados.iOS, búsqueda de servidor remoto con RestKit

Tengo dos problemas, 1. cómo debe ser mi asignación y 2. el json devuelve una matriz con dos tipos diferentes de objetos.

La URL tiene el siguiente aspecto:

search.json?search=[search string] 

El JSON devuelve el siguiente aspecto:

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

Dónde evento y noticias son dos tipos de objetos.

En mi aplicación tengo tres modelos, Post (entidad abstracta y superclase) NewsPost (subclase para Publicar) y Evento (subclase para Publicar).

Mis asignaciones se ve así:

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="]; 

Mi código de búsqueda se parece a esto (trabajos de búsqueda local):

- (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: '' 
} 

Algún consejo sobre la forma en que debería o podría hacer sería muy apreciada.

+0

Hola, estoy pensando en intentar esta pregunta. 1. ¿Puede cambiar el formato de JSON de alguna manera? Quiero saber si puede agregar una ruta de clave raíz a cada uno de los resultados (eso lo simplificará mucho). 2. Usted está usando NSManagedObject para Noticias y Eventos. ¿Pretendes guardar el mapeo en Coredata luego de la búsqueda del usuario? –

+0

Hola, sí, si es necesario, puedo cambiar el JSON. Y, sí, quiero almacenar en caché y guardar los resultados localmente cuando las búsquedas de los usuarios. – Anders

+0

Avísame si la respuesta te sirve. –

Respuesta

3

No he usado Objetos Administrados anteriormente, pero lo primero que debe hacer aquí es activar el registro del equipo de reposo sobre mapeo de objetos y solicitud de red para que pueda verificar qué es lo que obtiene el servidor del repositorio y cómo funciona la asignación.

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

En segundo lugar, de acuerdo a su JSON y que la búsqueda cambios de trayectoria, creo que es mejor usar el mapeo para la ruta de la clave en lugar de patrón de ruta del recurso. Por lo que debe tratar de mapear con llave, como en este ejemplo:

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"]; 

y luego cargar sus datos como:

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

La otra manera de hacer esto es para mapear por objeto, detecta de manera RestKit el tipo de objeto y realiza el mapeo y realiza la solicitud a cualquier ruta.

Si tiene alguna pregunta, por favor deje un comentario y puedo mejorar mi respuesta según sea necesario.

+0

Gracias, lo probaré mañana. – Anders

1

nunca he intentado responder a una pregunta con una recompensa antes, vamos a tratar de dar una respuesta útil a partir de algunos trabajos recientes =)

1. ¿Cómo debería parecerse a mi mapeo

De tu código, todo se ve bastante bien. ¿Hay algún anidamiento de objetos? ¿Necesita serializar para publicar de nuevo en el servidor?

2. el json devuelve una matriz con dos tipos diferentes de objetos.

Son sus atributos iguales (es decirEl evento tiene un título, el evento tiene una fecha) sin sorpresas. De lo contrario, debe usar dynamic nesting.

Si la ruta de un recurso (es decir, su ruta de búsqueda) recibe una colección con diferentes objetos (su caso), debe usar dynamic object mapping para cargar los objetos.

Dado que puede editar la estructura JSON, las cosas pueden ser más sencillas al aprovechar RestKit.

- Asegúrese de que el JSON tenga un root_key_path para los dos tipos de objetos diferentes.

A partir de un experimento antiguo y algunos de Google, RestKit puede asignar correctamente una salida json con diferentes objetos si tienen los rootKeyPath adecuados. El JSON resultante debe tener una estructura aproximada como:

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

No puedo estar seguro de que el 100% de lo anterior sea correcto. Puede experimentar haciendo que sus noticias de retorno API solo funcionen, luego agregue los datos de eventos a la mezcla.

- Cargar los objetos desde un servidor

// 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]; 

- Manejar el "real" que busca en su ObjectLoader Delegado

Si funcionó, los objetos debe correlacionarse con sus entidades CoreData. Puede realizar una búsqueda local utilizando el método NSPredicate que publicó anteriormente.

Prefiero el patrón de diseño donde RestKit usa loadObjects... para obtener datos del servidor y lo mapea, el resto del procesamiento se realiza localmente. Este desacoplamiento hace que las cosas sean más "parecidas a las aplicaciones". Puedes hacer otra forma de manipulación usando NSPredicates.

- (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. 
} 

Un ejemplo, si el caso de uso de búsqueda es restaurantes de la zona, es probable que tenga sentido para cargar todos los restaurantes dentro de la corriente de latitud/longitud, y luego realizar el filtrado local al nombre usando CoreData. Tu servidor te va a encantar.

Avíseme y trataré de mejorar la respuesta aún más.

+0

Gracias por su respuesta, y lo siento por mi respuesta tardía. De hecho lo hice funcionar usando @clopez answer con algunas modificaciones menores. – Anders

Cuestiones relacionadas