struggling with multi context core data, I would be very pleased for any help.
After the sync operations finished this block is called:
[[SyncEngine sharedEngine]setFinishedSyncCompletionBlock:^{
//---setting FRC to nil did refresh tableview but it is kinda slow
// self.fetchedResultsController=nil;
[self loadRecordsFromCoreData];
[self.tableView reloadData];
}];
And here I can see new managedobjects are in context. But calling [self.tableView reloadData]; after this not refreshes my tableview, or (void)controllerDidChangeContent:(NSFetchedResultsController *)controller not fires.
- (void)loadRecordsFromCoreData {
NSLog(#"here loadRecordsFromCoreData");
[self.managedObjectContext.parentContext performBlock:^{
//---this is also a trial for resetting FRC
// [self.managedObjectContext reset];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Event"];
// Edit the sort key as appropriate.
fetchRequest.sortDescriptors = #[ [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO] ];
NSArray *objects = [self.managedObjectContext.parentContext executeFetchRequest:fetchRequest error:NULL];
NSArray *objectIDs = [objects valueForKey:#"objectID"];
[self.managedObjectContext performBlock:^{
NSArray *objects = [self.managedObjectContext objectsWithIDs:objectIDs];
// self.fetchedResultsController.managedObjectContext = self.managedObjectContext;
NSLog(#"objects: %lu", (unsigned long)objects.count);
}];
}];
This is happening because reload table method is being called before loadRecordsFromCoreData's blocks are executed. Just make few changes, call [self.tableView reloadData]; after NSLog(#"objects: %lu", (unsigned long)objects.count); in self.managedObjectContext performBlock block.
Related
I have simply TODO list in which there are:
protocol's method reloadTableViewWhenItChanges;
fetch data (i.e. core data);
Table view reloads after stop/run compiler.
But I need to reload table view in viewDidLoad (not in viewWillAppear/viewDidAppear), using delegation table reloads only after something has changed in managedObjectContext.
How can I do that?
Here is my code (what is wrong in it? Where do I have to put [self.tableView reloadData] in my code, for example?):
- (void)viewDidLoad {
[super viewDidLoad];
self.title = #"Note List";
self.barButtonAddNote = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:#selector(addNewNote:)];
[self.navigationItem setRightBarButtonItem:self.barButtonAddNote animated:YES];
[self reloadTableViewWhenItChanges]; }
- (void)reloadTableViewWhenItChanges // protocol's method {
self.addNoteViewController.delegate = self;
if ([self.managedObjectContext hasChanges])
{
NSError *error;
[self.managedObjectContext save:&error];
}
[self fetchData];
[self.tableView reloadData]; }
- (void)fetchData {
NSFetchRequest *request = [[NSFetchRequest alloc]init];
NSEntityDescription *note = [NSEntityDescription entityForName:#"Note"
inManagedObjectContext:self.managedObjectContext];
[request setEntity:note];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"task" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
NSError *error = nil;
NSArray *mutableFetchResults = [self.managedObjectContext executeFetchRequest:request error:&error];
if (mutableFetchResults != nil){
self.notesArray = [[NSMutableArray alloc] initWithArray:mutableFetchResults];
} }
As mentioned by #eik_cloner, you don't need to reload table view in viewDidLoad because it's done automatically.
If you want to do it manually then you have to call reloadData method of UITableView wherever you want as:
[yourTableView reloadData];
In Objective C :-
[tableViewName reloadData];
In Swift :-
tableViewName.reloadData()
Make sure you have Configure the delegates with UIViewController through UIStoryboard or By code.
I'm triing to add NSManagedObject to existing data, but right after i did, my existing data disappears, and i cannot figure out why. Looking the code for like an hour couldnt help much :(
FirstTableViewController.m //DELEGATE points to AppDelegate
- (void)viewDidLoad {
[super viewDidLoad];
self.context=[DELEGATE managedObjectContext];
self.everyWish=[[NSMutableArray alloc]initWithArray:[DELEGATE collectSharedWishes]]; //collects every shared wishes from core data
self.emberek=[[NSMutableArray alloc]initWithArray:[DELEGATE collectUserNames]]; //collects every user
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:IDENTIFIER forIndexPath:indexPath];
User *emberek=[self.emberek objectAtIndex:indexPath.row];
NSMutableArray *currentUserWishes=[[NSMutableArray alloc]initWithArray:[DELEGATE collectCurrentUserSharedWishes:emberek]]; //collects the current user wishes only from core data
int completed=0;
for(SharedWishes *wish in currentUserWishes)
if([wish.wishIsPurchased isEqual:#1])
completed++;
cell.textLabel.text=emberek.userName;
cell.detailTextLabel.text=[NSString stringWithFormat:#"Wishes: %lu, purchased: %d", (unsigned long)currentUserWishes.count, completed];
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"pushToCurrentUser"]){
User *user=[self.emberek objectAtIndex:[[self.tableView indexPathForSelectedRow]row]];
SharedTableViewController *wishTVC=[segue destinationViewController];
wishTVC.user=user;
NSLog(#"%#", user);
}
}
OtherTableViewController.h
#property NSManagedObjectContext *context;
#property User *user;
OtherTableViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.context=[DELEGATE managedObjectContext];
self.format=[[NSDateFormatter alloc]init];
self.todayFormat=[[NSDateFormatter alloc]init];
self.results=[[NSMutableArray alloc]init];
self.wishes=[[NSMutableArray alloc]initWithArray:[DELEGATE collectCurrentUserSharedWishes:self.user]];
self.navigationItem.title=[NSString stringWithFormat:#"%#'s list", self.user.userName];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(refreshTableViewData) name:#"refreshTableViewData" object:nil];
self.refreshControl=[[UIRefreshControl alloc] init];
[self.refreshControl setTintColor:[UIColor redColor]];
[self.tableView addSubview:self.refreshControl];
[self.refreshControl addTarget:self action:#selector(pullDownToRefresh:) forControlEvents:UIControlEventValueChanged];
}
-(void)fetch:(CKRecord *)obj
{
NSError *error;
SharedWishes *wish=[NSEntityDescription insertNewObjectForEntityForName:#"SharedWishes" inManagedObjectContext:self.context];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"SharedWishes" inManagedObjectContext:self.context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"wishID==%#", obj.recordID.recordName];
[fetchRequest setPredicate:predicate];
NSArray *fetchedObjects = [self.context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil)
NSLog(#"Nem sikerult az ab-t elerni!, %#", [error localizedDescription]);
if([fetchedObjects isEqual:#[]])
{
User *currentUser=[[DELEGATE fetchUserWithID:self.user.userID] objectAtIndex:0]; //fetches the current user, which i already have, tried before using [self.user addSharedWishesObject:wish]
wish.wishName=obj[#"name"];
wish.wishPrice=obj[#"price"];
wish.wishIcon=obj[#"icon"];
wish.wishTime=obj[#"creationDate"];
wish.wishLocation=obj[#"location"];
wish.wishDescription=obj[#"description"];
wish.wishType=obj[#"type"];
wish.wishURL=obj[#"url"];
wish.wishImage=obj[#"image"];
wish.wishID=obj.recordID.recordName;
wish.wishIsPurchased=obj[#"purchased"];
wish.wishChangeTag=obj.recordChangeTag;
NSError *error;
[currentUser addSharedWishesObject:wish];
if(![self.context save:&error])
NSLog(#"Nem tudtam eltarolni az adatbazisban, oka: %#", [error localizedDescription]);
}
else
{
wish=[fetchedObjects objectAtIndex:0];
if(![wish.wishChangeTag isEqualToString:obj.recordChangeTag]){
wish.wishName=obj[#"name"];
wish.wishPrice=obj[#"price"];
wish.wishIcon=obj[#"icon"];
wish.wishTime=obj[#"creationDate"];
wish.wishLocation=obj[#"location"];
wish.wishDescription=obj[#"description"];
wish.wishType=obj[#"type"];
wish.wishURL=obj[#"url"];
wish.wishImage=obj[#"image"];
wish.wishID=obj.recordID.recordName;
wish.wishIsPurchased=obj[#"purchased"];
wish.wishChangeTag=obj.recordChangeTag;
//wish.user.userID=obj.creatorUserRecordID.recordName;
NSError *error;
if(![self.context save:&error])
NSLog(#"Nem tudtam eltarolni az adatbazisban, oka: %#", [error localizedDescription]);
}
}
}
Here is the code, it runs without an error, everything declared properly, although i didnt mention (in order to simplify my code).
After running, I have all the users, but only the last one's wishes appears, calling the NSMutableArray *currentUserWishes=[[NSMutableArray alloc]initWithArray:[DELEGATE collectCurrentUserSharedWishes:emberek]]; from FirsTableViewController. The last one has correct amount of objects, the previous ones have #"0 objects".
Saving them right in the OtherTableViewController? Any thoughts?
ps: posting more details if needed
BUG fixed:
Right after I saved the objects, I called a method, in which I compared my elelements (in my DB) to the cloud elements, and delete those which wasn't available any longer in the clouds from my persistent database. A "wrong" predicate caused the main issue because I queried all my database objects, instead of the particular user's elements
I need to implement cancel/save behavior for the Core Data objects. I have a UITableView for which I got data from NSFetchedResultsController.
- (void) configureWithCategoryItem: (CategoryItem *) categoryItem
{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:[CategoryItem entityName]];
request.predicate = [NSPredicate predicateWithFormat:#"categoryId = %#", categoryItem.categoryId];
request.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"title" ascending:YES]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:[CategoryItem defaultContext]
sectionNameKeyPath:#"title"
cacheName:nil];
[self.fetchedResultsController performFetch: NULL];
self.layers = [self categoryLayers];
self.sectionTitle = categoryItem.title;
}
Every click on the cell in this table view
LayerItem *layerItem = [self.layers objectAtIndex:indexPath.row];
layerItem.isSelected = [NSNumber numberWithBool:YES];
And in the navigation bar I have two buttons:
Cancel - I need to dismiss controller without saving the results to the core data,
Save - I need to save results and dismiss controller
The save method now looks like this:
- (void) saveChanges
{
NSManagedObjectContext *localContext = self.fetchedResultsController.managedObjectContext;
if ([localContext hasChanges]) {
[localContext save: NULL];
[[NSNotificationCenter defaultCenter] postNotificationName:kEADatabaseWasUpdated object:nil];
}
}
Is there some instrument in Core Data which can help me to implement this behavior?
I tried to use separate NSManagedObjectContext and mergeChangesFromContextDidSaveNotification but didn't receive good result.. Waiting for your help.
- (void) cancelChanges
{
NSManagedObjectContext *localContext = self.fetchedResultsController.managedObjectContext;
[localContext rollback];
}
I have read a lot of content and watched several tutorials on how to search Core Data by using a search bar however I have not yet seen anything for how to change a the sort descriptor by passing a setting from a Settings view to the Table View.
I have a search bar button item that when pressed goes to a SearchSettingsVC. The views communicate by passing boolean's from one VC to the other and vise-versa. The problem is that the table is not corresponding accordingly by changing the TableVC order - (I have tried calling self.tableview beginUpdates, self.tableview reload, self fetchedResultsController among other things).
The point is to reorder the TableVC results, not to present only specific results like a predicate does
I have created a delegate for the SettingsVC so that I can pass boolean value to the SettingsVC which is then capable of returning a different changed value if there are any changes.
The problem is that I cannot manage to get the table view (or prehaps even the fetched results) to update.
Here is my code for my -(NSFetchedResultsController*) fetchedResultsController method:
// return if already initialized
if (self.fetchedResultsController != nil) {
return self.fetchedResultsController;
}
if (dateSearch == true){
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
// the entity to fetch
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Details" inManagedObjectContext:managedObjectContext];
// how to sort the data
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"addDate" ascending:YES];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
// fetch the data
NSError *e = nil;
if (![self.fetchedResultsController performFetch:&e]) {
NSLog(#"fetch error(Date): %#", e );
abort();
}
NSLog(#"Dates loaded");
}
if (mostAmount == true){
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
// the entity to fetch
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Details" inManagedObjectContext:managedObjectContext];
// how to sort the data
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"amount" ascending:NO];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
// fetch the data
NSError *e = nil;
if (![self.fetchedResultsController performFetch:&e]) {
NSLog(#"fetch error (Most Fuel): %#", e);
abort();
}
NSLog(#"Amount loaded");
}
else{
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
// the entity to fetch
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Details" inManagedObjectContext:managedObjectContext];
// how to sort the data
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"addDate" ascending:YES];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
// fetch the data
NSError *e = nil;
if (![self.fetchedResultsController performFetch:&e]) {
NSLog(#"fetch error(Date): %#", e );
abort();
}
NSLog(#"Defualt loaded");
}
return self.fetchedResultsController;
I required a default because at the beginning when I initialise the booleans in the TableVC to false they can be updated when I go to my settings VC.
My SearchSettingsVC has UISwitches that change the values from true to false (and vice-versa) which successfully update the equivalent booleans in the TableVC as represented when I return to the SearchSettingsVC.
My prepareForSegue code in my TableVC
if ([[segue identifier] isEqualToString:#"searchSettings"]){
//pass new search settings in here
SearchSelectionSettings * settingsVC = (SearchSelectionSettings *)segue.destinationViewController;
settingsVC.delegate = self;
settingsVC.dateSearch = dateSearch;
settingsVC.mostAmount = mostAmount;
This is my closeSettings method which is located in my TableVC
#pragma mark - SettingsViewControllerDelegate methods
//record the settings changed in the settings view
//dismissViewController changes the screen
- (void)closeSettings:(id)sender {
NSLog(#"Working?");
dateSearch = ((SearchSelectionSettings *)sender).dateSearch;
mostAmount = ((SearchSelectionSettings *)sender).mostAmount;
[self dismissViewControllerAnimated:YES completion:nil];
[self FetchedResultsController];
NSIndexPath * indexPath;
[self tableView:self.tableView cellForRowAtIndexPath:indexPath];
}
Passing boolean values from one VC to other works perfectly. Getting the TableView to update according to the fetchedResults if statements does not, my switches in the SettingsVC are updated every time without any issues. Can anyone help or recommend a tutorial?
The opening lines of the fetchedResultscontroller getter, i.e.:
// return if already initialized
if (self.fetchedResultsController != nil) {
return self.fetchedResultsController;
}
mean that, once your fetchedResultsController has been created, the remaining code will not be executed when you access it. A quick way to resolve your problem would therefore be to set self.fetchedResultsController to nil in your closeSettings method, and then reload your table view. When the tableview reloads, it will access the fetchedResultsController again, and since it is now nil, the above code will be bypassed and your code will be used.
#pragma mark - SettingsViewControllerDelegate methods
//record the settings changed in the settings view
//dismissViewController changes the screen
- (void)closeSettings:(id)sender {
NSLog(#"Working?");
dateSearch = ((SearchSelectionSettings *)sender).dateSearch;
mostAmount = ((SearchSelectionSettings *)sender).mostAmount;
[self dismissViewControllerAnimated:YES completion:nil];
self.fetchedResultsController = nil;
[self.tableView reloadData];
}
Alternatively, you can modify the fetchedResultsController's fetch and then get it to reperform the fetch:
#pragma mark - SettingsViewControllerDelegate methods
//record the settings changed in the settings view
//dismissViewController changes the screen
- (void)closeSettings:(id)sender {
NSLog(#"Working?");
dateSearch = ((SearchSelectionSettings *)sender).dateSearch;
mostAmount = ((SearchSelectionSettings *)sender).mostAmount;
[self dismissViewControllerAnimated:YES completion:nil];
NSFetchRequest *request = [[NSFetchRequest fetchRequestWithEntityName:#"Details"];
NSSortDescriptor *sort;
if (dateSearch == true){
sort = [NSSortDescriptor sortDescriptorWithKey:#"addDate" ascending:YES];
NSLog(#"Dates loaded");
} else if (mostAmount == true) {
sort = [NSSortDescriptor sortDescriptorWithKey:#"amount" ascending:NO];
NSLog(#"Amount loaded");
} else {
sort = [NSSortDescriptor sortDescriptorWithKey:#"addDate" ascending:YES];
NSLog(#"Default loaded");
}
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
self.fetchedResultsController.fetchRequest = request;
// fetch the data
NSError *e = nil;
if (![self.fetchedResultsController performFetch:&e]) {
NSLog(#"fetch error (Most Fuel): %#", e);
abort();
}
[self.tableView reloadData];
}
That way, you can simplify your fetchedResultsController code so it just loads the default.
I'm working on a CoreData App which displays the content of the CoreData in a TableView.
The Problem is, after adding a string the tableView is not Updating the Content of the CoreData.
i tried it with:
- (void)viewWillAppear:(BOOL)none
{
[self.tableView reloadData];
}
But no Chance, its not reloading.
Any guesses or should I post some more Code? thank you :)
So more Code :)
Saving string to CoreData:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
AppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context =
[appDelegate managedObjectContext];
NSManagedObject *newProduct;
newProduct = [NSEntityDescription
insertNewObjectForEntityForName:#"Products"
inManagedObjectContext:context];
[newProduct setValue: _textField.text forKey:#"productName"];
NSError *error;
[context save:&error];
NSLog(#"Product saved");
}
And the Code of the TableViewController:
#implementation CartViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//Core Data Versuche
/*
Fetch existing events.
Create a fetch request for the Event entity; add a sort descriptor; then execute the fetch.
*/
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:#"Products"];
[request setFetchBatchSize:20];
// Order the events by creation date, most recent first.
// NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"creationDate" ascending:NO];
// NSArray *sortDescriptors = #[sortDescriptor];
// [request setSortDescriptors:sortDescriptors];
// Execute the fetch.
NSError *error;
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSArray *fetchResult = [delegate.managedObjectContext executeFetchRequest:request error:&error];
if (fetchResult== nil) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
// Set self's events array to a mutable copy of the fetch results.
[self setCartProductsArray:[fetchResult mutableCopy]];
/*
Reload the table view if the locale changes -- look at APLEventTableViewCell.m to see how the table view cells are redisplayed.
*/
__weak UITableViewController *cell = self;
[[NSNotificationCenter defaultCenter] addObserverForName:NSCurrentLocaleDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[cell.tableView reloadData];
}];
// Ende Core Data Versuche
}
- (void)viewWillAppear:(BOOL)none
{
[self.tableView reloadData];
}
What you probably want to do is use an NSFetchedResultsController.
Make sure that when you are updating the data, you are really just updating CoreData.
When things change in CoreData and you have an NSFetchedResultsController you can subscribe to updates. Apple has great documentation on how to make this work. These updates will update your table correctly using the delegation methods. This ends up mostly being a copy and paste from the documentation.