2012-03-01 8 views
17

Estoy queriendo usar RestKit para una aplicación que necesita funcionar tanto con conexión como sin conexión.Soporte en línea y fuera de línea para aplicaciones de RestKit iOS

Es posible que no esté entendiendo cómo funciona RestKit, pero pensé que esto era (bastante) fácil de implementar.

En una prueba de marcar iOS puse cosas de la siguiente manera:

// setup the client 
NSString* URL = @"http://10.211.55.5:3000/api"; 
RKClient* client = [RKClient clientWithBaseURL:URL]; 
client.username = @"[email protected]"; 
client.password = @"password"; 

// setup caching 
client.cachePolicy = RKRequestCachePolicyLoadIfOffline | RKRequestCachePolicyLoadOnError | RKRequestCachePolicyTimeout; 
client.requestCache.storagePolicy = RKRequestCacheStoragePolicyPermanently; 

// setup managed object store 
RKObjectManager* objectManager = [RKObjectManager objectManagerWithBaseURL:URL]; 
RKManagedObjectStore* objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:@"RestKitCoreDataTest.sqlite"]; 

// connect my cache implementation 
MyCache* cache = [[MyCache alloc] init]; 
objectStore.managedObjectCache = cache; 
objectManager.objectStore = objectStore; 

// setup mapping  
RKManagedObjectMapping* userMapping = [RKManagedObjectMapping mappingForClass:[User class]]; 
[userMapping mapKeyPath:@"id" toAttribute:@"identifier"]; 
[userMapping mapKeyPath:@"email" toAttribute:@"email"]; 
[userMapping mapKeyPath:@"firstname" toAttribute:@"firstname"]; 
[userMapping mapKeyPath:@"surname" toAttribute:@"surname"]; 
userMapping.primaryKeyAttribute = @"identifier"; 
[objectManager.mappingProvider setMapping:userMapping forKeyPath:@""]; 

// setup routes 
RKObjectRouter* router = [objectManager router]; 
[router routeClass:[User class] toResourcePath:@"https://stackoverflow.com/users/:identifier"]; 

el objeto de usuario se implementa como necesario para la compatibilidad CoreData:

@interface User : NSManagedObject 

@property (nonatomic, retain) NSNumber * identifier; 
@property (nonatomic, retain) NSString * email; 
@property (nonatomic, retain) NSString * firstname; 
@property (nonatomic, retain) NSString * surname; 

@end 

@implementation User 

@dynamic identifier; 
@dynamic email; 
@dynamic firstname; 
@dynamic surname; 

@end 

Aquí es MyCache. Tenga en cuenta que no me molesto en comprobar resourcePath ya que esto es solo para probar cosas, y tengo una ruta de todos modos.

@implementation MyCache 
- (NSArray*)fetchRequestsForResourcePath:(NSString*)resourcePath 
{ 
    NSFetchRequest* fetchRequest = [User fetchRequest]; 
    return [NSArray arrayWithObject:fetchRequest]; 
} 

-(BOOL)shouldDeleteOrphanedObject:(NSManagedObject *)managedObject 
{ 
    return true; 
} 
@end 

continuación hago una llamada al servidor para obtener el usuario con id 123, en la ruta "/ api/users/123":

User* user = [User object]; 
user.identifier = [NSNumber numberWithInt:123]; 
RKObjectManager* manager = [RKObjectManager sharedManager]; 
[manager getObject:user delegate:self]; 

Esto funciona bien. Pero cuando desconecto el wifi de mi Mac, el código anterior no recupera al usuario de la base de datos sqlite.

me sale el siguiente error en lugar de objectLoader:didFailWithError del delegado:

2012-03-01 11:44:09.402 RestKitCoreDataTest[1989:fb03] error: Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo=0x6b89aa0 {NSErrorFailingURLStringKey=http://10.211.55.5:3000/api/users/123, NSErrorFailingURLKey=http://10.211.55.5:3000/api/users/123, NSLocalizedDescription=The request timed out., NSUnderlyingError=0x6b81ac0 "The request timed out."} 

pensé que en virtud de que se especifica que la memoria caché se debe utilizar cuando hay un tiempo de espera, con: "RKRequestCachePolicyTimeout", que hubiera esperado el usuario que se recuperará de la memoria caché local.

La memoria caché contiene un registro de usuario con ID 123 - en la tabla ZUSER, con 123 en la columna ZIDENTIFIER.

¿Hay algún paso que me falta para que esto funcione? ¿Quizás otro método delegado que necesita ser manejado, o se llama, cuando la caché es golpeada después del tiempo de espera ? ¿O estoy tratando de hacer algo que no necesariamente es algo que obtendría "de fábrica" ​​con RestKit?

Saludos.

+0

Hola , Estoy tratando de hacer lo mismo con RestKit 0.20.1 - ¿Alguna actualización sobre esto? ¿conseguiste que esto funcionara? La respuesta que marcó no parece responder a su pregunta ... ¡Gracias! – Diego

+1

Revisé la respuesta a continuación como correcta porque tomé la sugerencia de Julian de utilizar una clase de alcance para verificar si estaba en línea o fuera de línea. Más tarde terminé usando RestKit para comunicaciones en línea, y CoreData para almacenamiento fuera de línea. – dbarros

+0

¡Bien, gracias! Creo que intentaré centralizar esto en el delegado de la aplicación, aunque para hacerlo creo que es mejor si creo una cola de operación HTTP que luego analizo cada vez que se necesita sincronización (posiblemente periódicamente). – Diego

Respuesta

6

Puede usar la Clase de accesibilidad para determinar si su cliente está fuera de línea o no. Uso esta gran clase muy a menudo en cada proyecto que requiere una conexión a Internet.

Simplemente inicia el notificador a un host específico. En todo su viewController ahora solo tiene que registrar los métodos en NSNotificationCenter para establecer un BOOL isOnline por ejemplo.

Al hacer esta práctica, puede hacer cosas hermosas en su aplicación como superponer la aplicación con un mensaje "Sin conexión".

https://gist.github.com/1182373

EDITAR

Aquí está un ejemplo de la pantalla de inicio de sesión desde uno de mis proyectos (lo siento por esta cantidad de código, pero este es mi completa aplicación):

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{ 
    //// Some stuff //// 
    [[Reachability reachabilityWithHostname:@"apple.com"] startNotifier]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil]; 
} 
- (void)reachabilityChanged:(NSNotification *)note 
{ 
    if ([[note object] isReachable]) { 
     CAKeyframeAnimation *scale = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"]; 
     [scale setValues:[NSArray arrayWithObjects:[NSNumber numberWithFloat:1.0], [NSNumber numberWithFloat:0.0], nil]]; 
     [scale setDuration:0.3]; 
     [scale setRemovedOnCompletion:NO]; 

     [[offlineView layer] setTransform:CATransform3DMakeScale(0, 0, 1.0)]; 
     [[offlineView layer] addAnimation:scale forKey:@"scale"]; 
     [[offlineView layer] setTransform:CATransform3DIdentity]; 

     [UIView animateWithDuration:0.3 animations:^{ 
      [offlineView setAlpha:0]; 
     } completion:^(BOOL finished){ 
      if (finished) { 
       [offlineView removeFromSuperview]; 
      } 
     }]; 

     [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES]; 
    } 
    else { 
     CGRect screenFrame = CGRectMake(0, 0, 320, 480); 

     offlineView = [[UIView alloc] initWithFrame:screenFrame]; 
     [offlineView setBackgroundColor:[UIColor colorWithWhite:0 alpha:0.7]]; 
     [offlineView setAlpha:0]; 

     offlineLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 30)]; 
     [offlineLabel setFont:[UIFont fontWithName:@"Verdana" size:30.0]]; 
     [offlineLabel setBackgroundColor:[UIColor clearColor]]; 
     [offlineLabel setTextAlignment:UITextAlignmentCenter]; 
     [offlineLabel setTextColor:[UIColor whiteColor]]; 
     [offlineLabel setCenter:[offlineView center]]; 
     [offlineLabel setText:@"OFFLINE"]; 

     [offlineView addSubview:offlineLabel]; 
     [[self window] addSubview:offlineView]; 

     [UIView animateWithDuration:0.3 animations:^{ 
      [offlineView setAlpha:1.0]; 
     }]; 

     [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent animated:YES]; 
    } 
} 
+0

He usado este método, pero te advierto sobre una cosa. El Centro de notificaciones entregará el aviso en un orden "aleatorio". Por lo tanto, un ViewController lo obtendrá y otro más tarde, lo que generará incoherencias en su aplicación. Tal vez se ejecutará entre dos notif: en ViewController ver "en línea", otro ver "fuera de línea" ... Sugiero que solo una clase, tal vez su AppDelegate, se registre y luego toda la aplicación verifique a través de una API interna a este clase. Ahora tendrá una vista coherente para toda su clase. – malaba

+0

@malaba - ¡Tienes toda la razón! Estoy gestionando la accesibilidad desde AppDelegate también –

+2

puedes registrarte para 'RKReachabilityDidChangeNotification' y jugar con la clase' RKReachabilityObserver' para usar cosas de RestKit. – malaba

0

Compruebe la versión de RestKit; en la versión más reciente de RestKit hay un enfoque ligeramente modificado para obtener los datos en caché del almacén de objetos administrados. No tengo el ejemplo en esta máquina; pero si necesita más ayuda (ya que esta es una pregunta bastante antigua) por favor responda.

Cuestiones relacionadas