{"id":3182,"date":"2015-02-05T09:57:01","date_gmt":"2015-02-05T15:57:01","guid":{"rendered":"http:\/\/benincosa.com\/?p=3182"},"modified":"2015-02-05T09:57:44","modified_gmt":"2015-02-05T15:57:44","slug":"ios-managedobject-doesnt-trigger-nsfetchresultcontroller-reload","status":"publish","type":"post","link":"https:\/\/benincosa.com\/?p=3182","title":{"rendered":"iOS: ManagedObject doesn&#8217;t trigger NSFetchResultsController Reload"},"content":{"rendered":"<p>One of the problems I have been working on is to update a UITableViewCell with updates after the parent view controller reappears. \u00a0In this case the user clicks on a &#8216;comments&#8217; button which segues to a new view controller where the user enters comments for the post. \u00a0Once the user enters these comments, they click the back button to return to the previous view controller. \u00a0That view controller should have one of its table cells updated with the new comment.<\/p>\n<p>But it doesn&#8217;t work!<\/p>\n<p>I tried several solutions and spent a few hours on this. \u00a0There were some helpful posts explaining what happened such as <a href=\"http:\/\/stackoverflow.com\/questions\/12370521\/changing-a-managedobject-property-doesnt-trigger-nsfetchedresultscontroller-to\">this one<\/a>\u00a0and <a href=\"http:\/\/wangling.me\/2011\/09\/bugs-of-nsfetchedresultscontrollerdelegate-template-code.html\">this one<\/a>.\u00a0\u00a0 None of those seemed to work for me.<\/p>\n<p>I started out creating an unwind segue on the main view controller:<\/p>\n<pre class=\"lang:objc decode:true \">\/\/ This action is called when we come back from a comment.  We defined an exit segue\r\n\/\/ and the commentViewController called it.  Here we now determine which comment was updated\r\n\/\/ and refresh that cell.\r\n- (IBAction)unwindComment:(UIStoryboardSegue *)segue    {\r\n    NSLog(@\"Just came back from comments!\");\r\n    \/\/segue.sourceViewController\r\n    \/\/FeedCommentTableViewCell *cell = (FeedCommentTableViewCell *)[self.tableView cellForRowAtIndexPath:self.selectedCellIndexPath];\r\n    \r\n    \/\/Post *p = (Post *)[self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:self.selectedCellIndexPath.section]];\r\n    \r\n    \/\/[p willChangeValueForKey:@\"comments\"];\r\n    \/\/[p didChangeValueForKey:@\"created_at\"];\r\n    \r\n    \/\/[self configureCommentCell:cell withPost:p];\r\n    \/\/ This selctedCellIndexPath was set when we segued to the comments controller.\r\n    \/\/[self.tableView reloadRowsAtIndexPaths:@[self.selectedCellIndexPath] withRowAnimation:UITableViewRowAnimationFade];\r\n}<\/pre>\n<p>And as you can see from all the commented out garbage above, I tried just about everything to get that table cell to reload. \u00a0The curious thing was that the size did change, but the contents did not.<\/p>\n<p>For completeness, on the exit for the comment view controller I had the code call this segue:<\/p>\n<pre class=\"lang:objc decode:true \" title=\"child view controller exit calls the segue\">- (void)viewWillDisappear:(BOOL)animated {\r\n    [super viewWillDisappear:animated];\r\n    UIViewController *vc = self.navigationController.topViewController;\r\n    if ([vc class] == [SMFeedViewController class]) {\r\n        \/\/\r\n        [self performSegueWithIdentifier:@\"unwindComment\" sender:self];\r\n    }\r\n}<\/pre>\n<p>(Note: In storyboard you have to control drag from this view controller to the exit and select the &#8220;unwindComment&#8221; segue for this to be hooked up)<\/p>\n<p>Well, none of that worked. \u00a0What I found was that the new comment wasn&#8217;t being saved in time for when the segue unwind was called. \u00a0I think if I put that segue in viewDidDisappear it might work as that is called later but I&#8217;m happy with my solution so won&#8217;t change it.<\/p>\n<p>Instead the way I solved it was by putting in a notification in my Data Model when the comment was saved.<\/p>\n<pre class=\"lang:default decode:true\" title=\"DataManager.m \">- (void)saveBackgroundContext {\r\n    [self.backgroundContext performBlockAndWait:^{\r\n        NSError *error = nil;\r\n        BOOL saved = [self.backgroundContext save:&amp;error];\r\n        NSLog(@\"error: %@\", error);\r\n        if (!saved) {\r\n            \/\/ do some real error handling\r\n            NSLog(@\"Could not save background context due to %@\", error);\r\n        }else {\r\n            NSLog(@\"Saved Background context\");\r\n            [[NSNotificationCenter defaultCenter] postNotificationName:@\"Saved Background context\" object:nil];\r\n            \r\n        }\r\n    }];\r\n}<\/pre>\n<p>Here when its saved, we post a notification saying we saved something.<\/p>\n<p>Now, back on the main view controller (where there is no editing) we listen for this notification:<\/p>\n<pre class=\"lang:objc decode:true \" title=\"In the view did load method. \">[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundUpdate:) name:@\"Saved Background context\" object:nil];<\/pre>\n<p>Then we update the tableview when this is fired:<\/p>\n<pre class=\"lang:default decode:true \">- (void)backgroundUpdate:(NSNotification *)notification {\r\n    if (self.selectedCellIndexPath) {\r\n        FeedCommentTableViewCell *cell = (FeedCommentTableViewCell *)[self.tableView cellForRowAtIndexPath:self.selectedCellIndexPath];\r\n        Post *p = (Post *)[self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:self.selectedCellIndexPath.section]];\r\n        [self configureCommentCell:cell withPost:p];\r\n    }\r\n}<\/pre>\n<p>What a relief it was to see this work!<\/p>\n<p>Now when the user clicks the back button after posting a comment, the main tableview is updated with that comment.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of the problems I have been working on is to update a UITableViewCell with updates after the parent view controller reappears. \u00a0In this case the user clicks on a &#8216;comments&#8217; button which segues to a new view controller where the user enters comments for the post. \u00a0Once the user enters these comments, they click&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[995],"tags":[672,674,673],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3182"}],"collection":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3182"}],"version-history":[{"count":2,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3182\/revisions"}],"predecessor-version":[{"id":3185,"href":"https:\/\/benincosa.com\/index.php?rest_route=\/wp\/v2\/posts\/3182\/revisions\/3185"}],"wp:attachment":[{"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3182"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3182"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/benincosa.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3182"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}