Después de leer docenas de preguntas similares, me gustaría comenzar con esta afirmación: "Hice el delegado del NSFetchedResultsController, no funcionó". .NSFetchedResultsController no ve nuevos insertos/elimina los valores obtenidos después de la actualización
Tengo un TableViewController simple cuyas celdas están llenas de NSFetchedResultsController. Aquí está el código FRC init:
@property (strong, nonatomic) NSFetchedResultsController *frc;
...
- (NSFetchedResultsController *)frc
{
if (!_frc)
{
NSError *error = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:e_product];
request.sortDescriptors = [NSArray arrayWithObjects:
[NSSortDescriptor sortDescriptorWithKey:@"product_group.product_group_name" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:f_product_name ascending:YES],
nil];
_frc = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:[DTGGlobalSettings sharedInstance].moc sectionNameKeyPath:@"product_group.product_group_name" cacheName:nil];
_frc.delegate = self;
[_frc performFetch:&error];
if (error)
{
NSLog(@"Error while fetching products: %@!", error.userInfo);
}
}
return _frc;
}
También he métodos de delegado NSFetchedResultsController implementado con algunas etiquetas de depuración:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
NSLog(@"controllerWillChangeContent");
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
NSLog(@"didChangeObject - insert");
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
NSLog(@"didChangeObject - delete");
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
NSLog(@"didChangeObject - update");
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
NSLog(@"didChangeObject - move");
[tableView deleteRowsAtIndexPaths:[NSArray
arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray
arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
NSLog(@"didChangeSection - insert");
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
NSLog(@"didChangeSection - delete");
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
NSLog(@"controllerDidChangeContent");
[self.tableView endUpdates];
}
En mi navigationbar tengo el botón de actualización, su función es la de encender el catálogo de productos método que hace lo siguiente: 1. Obtiene productos del servidor MySQL remoto 2. Si el producto con ID no existe, lo crea y rellena todos los campos 3. Si existe, todos los campos se actualizan de acuerdo con el valores obtenidos
Después de la fila 2 y 3 se guarda la tienda. Utilizo NSManagedObjectContext para todas las operaciones de CoreData en la aplicación.
Cuando inicio la aplicación por primera vez, TVC está vacío, porque todavía no se han recuperado los productos. Cuando presiono Actualizar, los nuevos productos se extraen y se insertan en ManagedObjectContext, pero NSManagedObjectContext no ve/escucha ningún cambio: no se invocan métodos de delegado, por lo que no se agregan nuevas filas a TVC. Cuando reinicio la aplicación, los nuevos productos están en su lugar en el TVC sin ningún problema.
Si presiono el botón Actualizar una vez más cuando hay algunos productos allí, después de una breve demora, todos desaparecen. Algunas depuraciones me mostraron que si sucede después de actualizar los campos de las entidades (en realidad los valores son los mismos) y guardar la tienda. Los métodos delegar esta vez funcionan como un amuleto y para cada controlador de entidad actualizado y guardado: didChangeObject: atIndexPath: forChangeType: newIndexPath se llama con forChangeType = NSFetchedResultsChangeDelete.
Pregunta n. ° 1: ¿por qué NSFetchedResultsController no ve las entidades insertadas sin reiniciar la aplicación? Pregunta # 2: ¿por qué NSFetchedResultsController marca entidades ya recuperadas como "no existentes" (?) Y las elimina de TVC después de guardar en la tienda?
Le agradecería cualquier ayuda e ideas, siendo la lucha con este problema para toda la semana pasada; (
UPD1 (por Jody): Aquí están algunos detalles que utilizan DTGGlobalSettings clases personalizadas para compartir algunas vars. . a través de la aplicación Así es como me init su propiedad sharedInstance:
+(DTGGlobalSettings *)sharedInstance
{
static DTGGlobalSettings *myInstance = nil;
if (nil == myInstance)
{
myInstance = [[[self class] alloc] init];
// set values here
// try to acces MOC to init CoreData
[myInstance moc];
myInstance.baseURL = @"http://local.app/";
}
return myInstance;
}
a init pila CoreData utilicé un ejemplo de la documentación de Apple, porque por alguna razón no veo la casilla de verificación "Usar CoreData" al crear un nuevo proyecto. Estoy seguro de haberlo visto antes en Xc oda, pero ahora no lo hago; ((Xcode 4.4.1):
#pragma mark Core Data stack
- (NSManagedObjectContext *) moc {
if (_moc != nil) {
return _moc;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_moc = [[NSManagedObjectContext alloc] init];
[_moc setPersistentStoreCoordinator: coordinator];
}
return _moc;
}
- (NSManagedObjectModel *)managedObjectModel {
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
_managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *storeUrl = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
storeUrl = [storeUrl URLByAppendingPathComponent:DOC_NAME];
NSError *error;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error]) {
NSLog(@"Error adding a store to the coordinator: %@, %@", error, error.userInfo);
}
return _persistentStoreCoordinator;
}
-(void)saveDataStore
{
NSError *error;
if (![self.moc save:&error]) NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
El 'moc' (ManagedObjectContext) Propiedad de DTGGlobalSettings se utiliza entonces por todas partes en la aplicación donde necesito acceso a CoreData.
Ahora a la segunda parte, actualizando la base de datos.Obtengo algunas entidades nuevas en formato JSON desde un servidor web remoto, revisando el diccionario resultante y llamando al método createFromDictionary para cada entidad que se encuentra en los resultados de la solicitud. Aquí está el código:
+(Product *)createFromDictionary:(NSDictionary *)entityData inContext:(NSManagedObjectContext *)moc performUpdate:(BOOL)update
{
Product *result;
if (update)
{
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:e_product];
request.predicate = [NSPredicate predicateWithFormat:@"%K = %@", f_product_id, [entityData objectForKey:f_product_id]];
result = [[DTGGlobalSettings sharedInstance].moc executeFetchRequest:request error:nil].lastObject;
if (!result)
{
NSLog(@"Error updating Product ID %@! Cannot fetch entity.", [entityData objectForKey:f_product_id]);
return nil;
}
} else
{
result = [NSEntityDescription insertNewObjectForEntityForName:e_product inManagedObjectContext:[DTGGlobalSettings sharedInstance].moc];
}
result.product_id = [[DTGGlobalSettings sharedInstance].numFormatter numberFromString:[entityData objectForKey:f_product_id]];
result.product_image = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[DTGGlobalSettings sharedInstance].baseURL stringByAppendingString:[entityData objectForKey:f_product_image]]]];
result.product_name = [entityData objectForKey:f_product_name];
return result;
}
Cuando me quedo sin entidades fethed, llamo [[DTGGlobalSettings sharedInstance] saveDataStore] (véase más arriba). En este punto, desaparecen las filas existentes en TVC (si hubo alguna). En caso de que agregara algunas entidades nuevas, en el momento de guardar nada sucede, es decir, TVC no actualiza sus filas.
Ha publicado el código incorrecto. Los aspectos interesantes son cómo se importan los datos en su base de datos y cómo se configura la pila de datos básicos para la importación y el controlador de resultados obtenidos. –
Agregué algunos detalles que solicitó en UPD1. Espero que ahora esté más claro cómo terminé con esto ... – Arseniy
¿Alguna idea sobre la actualización? – Arseniy