0
votes

I'm trying to update a UITableView in a view called "HomeViewController" to show the new objects that I've added. But the NSFetchedResultsController doesn't update after I've finished creating a new one. I have the following code in various documents:

HomeViewController.h

- (void)saveNewShindyToDatabaseForName:(NSString *)name
                               details:(NSString *)details
                           dateAndTime:(NSDate *)dateAndTime
                              location:(CLLocation *)location
                            timePosted:(NSDate *)postedTime
{

    if (!self.shindysDatabase) {
        NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
        self.shindysDatabase = [[UIManagedDocument alloc] initWithFileURL:url];
    }

    dispatch_queue_t saveQ = dispatch_queue_create("Shindy Creator", nil);
    dispatch_async(saveQ, ^{
        [self.shindysDatabase.managedObjectContext performBlock:^{
            [Shindy setupShindyForSaveToDatabaseForName:name
                                                details:details
                                            dateAndTime:dateAndTime
                                               location:location
                                             timePosted:postedTime
                                 inManagedObjectcontext:self.shindysDatabase.managedObjectContext];
        }];
    });

    NSError *error = nil;
    if (![self.shindysDatabase.managedObjectContext save:&error]) {
        NSLog(@"%@", error.userInfo);
    }
}

- (void)setupFetchedResultsController
{
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Shindy"];
    NSSortDescriptor *nameSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
    NSSortDescriptor *photoSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"photo" ascending:YES];
    NSSortDescriptor *detailSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"details" ascending:YES];
    NSSortDescriptor *dateAndTimeSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"dateAndTime" ascending:YES];
    NSSortDescriptor *timePostedSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"timePosted" ascending:YES];
    request.sortDescriptors = [NSArray arrayWithObjects:nameSortDescriptor, photoSortDescriptor, detailSortDescriptor, dateAndTimeSortDescriptor, timePostedSortDescriptor, nil];

    self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                                                        managedObjectContext:self.shindysDatabase.managedObjectContext
                                                                          sectionNameKeyPath:nil
                                                                                   cacheName:nil];
}

Shindy+SQL.m (An NSManagedObject subclass of the entity, "Shindy")

+ (Shindy *)setupShindyForSaveToDatabaseForName:(NSString *)name
                               details:(NSString *)details
                           dateAndTime:(NSDate *)dateAndTime
                              location:(CLLocation *)location
                            timePosted:(NSDate *)postedTime
                inManagedObjectcontext:(NSManagedObjectContext *)context
{
    Shindy *shindy = [NSEntityDescription insertNewObjectForEntityForName:@"Shindy" inManagedObjectContext:context];

    __block NSString *nameString = name;
    __block FBProfilePictureView *profileImage = nil;

    if (FBSession.activeSession.isOpen) {
        [[FBRequest requestForMe] startWithCompletionHandler:
         ^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
             if (!error) {
                 nameString = user.name;
                 profileImage.profileID = user.id;
             }
         }];
    }

    NSData *imageData = nil;

    for (NSObject *obj in [profileImage subviews]) {
        if ([obj isMemberOfClass:[UIImageView class]]) {
            UIImageView *objImg = (UIImageView *)obj;
            imageData = UIImagePNGRepresentation(objImg.image);;
            break;
        }
    }

    shindy.name = name;
    shindy.dateAndTime = dateAndTime;
    shindy.timePosted = postedTime;
    shindy.details = details;
    shindy.photo = imageData;

    return shindy;
}

The method, - (void)setupFetchedResultsController: is called when the view is initially loaded (but not in the viewDidLoad or viewWillAppear method). And my - (void)saveNewShindyToDatabaseForName: function is called in a separate view controller once the user is finished entering their data. In other words, it's public API, but the NSManagedObjectContext remains within the HomeViewController for delegation. Also, the delegates & data sources for my code are set properly.

I have tried to force every kind of fetch; whether it be through the managedObjectContext, or the fetchedResultsController. I have also set an NSLog in various places of my code to ensure that the data is getting through to the HomeViewController. I have also set another NSLog to ensure that both the fetchedResultsController & managedObjectContext is not nil.

Everything seems to check out, and I don't get any sort of errors from the other default errors currently within my code.

I fear that this has to do something with the NSSortDescriptors I have in my - (void)setupFetchedResultsController: method, but I have also tried to configure those in various ways to no avail. There is a definite connection to my managedObjectContext & my fetchedResultsController as well, so that's not the problem.

I've simply run out of things to try. Perhaps, someone on here has some insight.

For jrturton...

I have implemented the following code in my - (void)saveNewShindyToDatabaseForName: method after all of the code that was there originally. It looks like this:

- (void)saveNewShindyToDatabaseForName:(NSString *)name
                               details:(NSString *)details
                           dateAndTime:(NSDate *)dateAndTime
                              location:(CLLocation *)location
                            timePosted:(NSDate *)postedTime
{
    // All of the code originally in this method is here...

    if ([self.fetchedResultsController managedObjectContext] == self.shindysDatabase.managedObjectContext) {
        NSLog(@"MOC's are the same.");
    }

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Shindy"];
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"details" ascending:YES];
    request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];

    NSError *error = nil;
    NSArray *detailsArray = [self.shindysDatabase.managedObjectContext executeFetchRequest:request error:&error];

    if (error) {
        NSLog(@"error: %@", error.userInfo);
    }

    NSLog(@"details: %@", detailsArray);
}

What all of this returns is that to answer your second possible answer, the MOC's are definitely the same. However, it appears as though the new objects are not actually getting saved into Core Data. Or I'm not pulling them back out properly.

The only way I could think of logging what is inside of my Core Data without getting an unrecognized selector error was by creating a new request for the correct entity, and then executing a fetch, then logging the results of that fetch, which, in this case, returned nothing.

I'm going to assume then that my problem lies within my code not saving the new objects to Core Data. With that said, can you identify anywhere I'm not properly saving these objects to my context so that it can be put into my Core Data model for retrieval later? Or do I need to setup a different method other than requesting to log what's being saved into my Core Data?

1

1 Answers

2
votes

Possible issues:

  1. No fetched results controller delegate (ok, resolved according to comments).
  2. The FRC is potentially being set up before you have a database (you create a new database inside the first method, but say the FRC is set up before that - do they have the same MOC object?
  3. Multithreading hell - your MOC save call will be executed before the block that calls the method that calls the method that passes the completion block (phew!). This could well result in the notifications not catching up.

You don't mention if the new objects ever enter the database - if you save, close and re-open the app, are they there? Are we dealing with a failure to send or receive change notifications, or a failure to make the changes?