UITableView datasource methods called before database open - iphone

I am writing an application that uses a Core Data database to store data which I then want to display using a UITableView. I have everything working, but was a little curious if there is a way around one small point that is bugging me...
When the app runs I do the following:
Create a NSManagedDocument
Create a NSFetchedResultsController
Open the Core Data database.
I am using ...
[[self testDatabase] openWithCompletionHandler:^(BOOL success) {
if(success) {
...
}
}];
to open the database but my problem is that by the time the block has executed the UITableView dataSource has already called -[TableViewController tableView:numberOfRowsInSection:] and returned rows=0
My solution so far has been to ignore this first "automatic" call and instead add a performFetch and a reloadData to the block that executes when the database is open.
[[self testDatabase] openWithCompletionHandler:^(BOOL success) {
if(success) {
[[self fetchedResultsController] performFetch:nil];
[[self tableView] reloadData];
}
}];
My question, is there a way to stop or delay that first call? or is there something I should add to -[TableViewController tableView:numberOfRowsInSection:] to manage that first call, or does it simply not matter and its fine as it is?

You can try starting with nil for tableView.dataSource and setting it after fetch.

Related

Core data edit function

I already have a tableView with data in it. IF you tap a cell/row it pushes to an edit type of view. Is there anyway to edit core data's data other than: By edit, i mean i already have data inserted into my context. I have loaded my data into my view, the user can change the existing data, and re save it.
.h
//Below is the entity/entity's class name 'Amm'
Amm *amm;
.m
-(IBAction)save
{
[self.amm setValue:self.nameField.text forKey:#"name"];
[self.amm setValue:self.nicknameField.text forKey:#"nickname"];
[self.navigationController popViewControllerAnimated:YES];
NSError *error;
if (![self.managedObjectContext save:&error]) {
//Handle Error
}
}
I want this code to work, however the design pattern of my app isnt allowing this code to work for me as it does in other parts of my app. Thank you very much for any and all help!
I assume from what you've said you have:
A table view listing your managed objects
A view where you can edit the values of a managed object
A save button bound to the save method
What's the actual issue? I'm assuming when you tap save that:
The values in self.nameField.text isn't setting self.amm.name
The values in self.nicknameField.text isn't setting self.amm.nickname
Is that right? If so perhaps try the following code to set the managed object values:
self.amm.name = self.nameField.text
self.amm.nickname = self.nicknameField.text
If that's not the issue and you are actually setting the managed object values properly, is it that you just need to refresh the table-view? Perhaps use some NSLog commands to log every step of the applications progress.

app crashes when deleting an object from core data in iphone

I have populated a table view with some data from server and saved it to the core data.Now i have to delete the object from the core data when the user clicks on the delete option in the table view.
What i have tried is this:`
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSError *error;
[[Server serverInfo].context deleteObject:[self.couponList objectAtIndex:indexPath.row]];
if(![ [Server serverInfo].context save:&error]) {
// Handle error
NSLog(#"Unresolved error series %#, %#", error, [error userInfo]);
}
[self.couponList removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
if ([self.couponList count]==0) {
[self.table setEditing:NO animated:YES];
[self.editBt setStyle:UIBarButtonItemStyleBordered];
}
}
`
But it gives an exception and crashes.This is i am getting in the log :"Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An NSManagedObjectContext cannot delete objects in other contexts".'Can anyone solve this?Thanks in advance
you have to make some manageobject context, a fetch request and then remove object using some predicate.
Apparently you are using more than one managed object context. This is indicated by your error message. Make sure that you are using only one managed object context, i.e. that there are no background tasks that use a different one.
You are keeping the data for your table view in a separate array. This might be another problem. The proper way to deal with core data and table views is to employ the NSFetchedResultsController.
Agree with Mundi.
On top of that, if you require many instances of the managedObjectContext, do not create that many but rather use the lock and unlock functions of the NSManagedObjectContext to enable multi-threading without issues on faults and invalidation of the objects.
EDIT
Maybe you can try creating just one NSManagedObjectContext in the AppDelegate and call the same managedObjectContext from the controllers where you need to use them. This, and together with the lock and unlock methods solved my issue with the multi-threading and invalidation of the objects.
Hope this helps.

Restkit Core data integration with NSManagedObjectContext

For the last weeks I am learning Restkit (v0.10.0) and core data and the possibilities are endless with these great tools. Problem is I am a bit overwhelmed on how to see the bigger picture here. And because of the very fast paced updating of Restkit most of the tutorials/demo code is out of date and not working properly any more.
I have managed to get my tableview filled with data from my json on a remote server. I also worked out on how to make the remote data leading in combination with caching working now, but I am struggling with the NSManagedObjectContext/NSEntityDescription (Core data) and how it works out with Restkit when using POST commands.
If I understand it correctly the record is created in Core Data (after the comment line // Create a new instance ) and after that that data is used to create a POST request so that the record is posted to the server.
This code is being used to create a new record on the server but when the code is executed (I see a record being created on my server) but my tableview is not updated accordingly, the table view is not updated and therefore the new record is first visible when restarting the app. Manually refreshing the data from the server does not help either.
Hopefully someone can give me some pointers, or maybe a tutorial with Restkit/core data and a POST combined. Thanks!
- (void)createGoalWithName:(NSString *)name andDescription:(NSString *)goalDescription
{
Goal* goal = [Goal object];
goal.identifier = 0;
goal.name = name;
goal.goalDescription = goalDescription;
// Create a new instance of the entity managed by the fetched results controller.
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
[NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
[self saveContext];
[[RKObjectManager sharedManager].router routeClass:[Goal class] toResourcePath:#"/api/goals" forMethod:RKRequestMethodPOST];
[[RKObjectManager sharedManager] postObject:goal delegate:self];
[self.tableView reloadData];
}
- (void)saveContext {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSError *error = nil;
if (![context save:&error]) {
/*
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.
If it is not possible to recover from the error,
display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
You have to use blocks when using RestKit+CoreData, and forget the router setup:
NSString *postUrl = #"/someurl/newelement";
[ [RKObjectManager sharedManager] loadObjectsAtResourcePath:postUrl usingBlock:^(RKObjectLoader* loader) {
loader.serializationMIMEType = RKMIMETypeJSON;
loader.delegate = nil;
loader.targetObject = nil;
loader.method= RKRequestMethodPOST; // change to GET, POST, PUT etc
}];
Since you don't include the UI code, it's hard to diagnose this problem fully, but one thing that might be happening since the updates are showing up when you restart the app, is that you're not properly synchronizing changes between the various thread's local managed object contexts. RestKit has its own managed object context since it doesn't run on the main UI thread.
The concept of working with multiple threads in Core Data is covered in this Apple document: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/CoreData/Articles/cdConcurrency.html but the gist of it is that you need to register for the notification NSManagedObjectContextDidSaveNotification and then invoke mergeChangesFromContextDidSaveNotification: on the UI thread's managed object context to safely merge the changes done on the RestKit thread.
Keep in mind that the notification will be posted on the RestKit thread, so you probably have to run the update on the main UI thread, e.g. something like this in the method receiving the notification:
[self.managedObjectContextForMainThread performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:) withObject:notification waitUntilDone:YES];
Where the property NSManagedObjectContext* managedObjectContextForMainThread has been properly initialized to point to the UI thread's managed object context.
Hope this helps (if you haven't abandoned RestKit altogether...)
I'm still using a slightly older version of Restkit. But one key element is that a primary key attribute must be defined.
So that Restkit can keep your local stored objects and server objects in sync.
In your case, when defining mappings for your Goal object, you would do it like so:
goalMapping.primaryKeyAttribute = #"identifier";

Update and delete entities in two different process

I'm working on an ipad application that use coredata. It download information on a database that is on the web, and record them in coredata. The application is based on a split view. My problem was to make the update of the data in background. Here is how I've done :
- I've create an NSOperation, that does the download and the update of the data.
- This NSOperation use a different NSManagedObjectContext than the context of the appDelegate, return by this function, that is in the appDelegate :
(NSManagedObjectContext*)newContextToMainStore {
NSPersistentStoreCoordinator *coord = nil;
coord = [self persistentStoreCoordinator];
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
[moc setPersistentStoreCoordinator:coord];
return [moc autorelease];
}
- I've had an observer in the NSOperation, that will call this function in the appDelegate when I save the context, to modify the context of the delegate too :
- (void)mergeChangesFromContextSaveNotification:(NSNotification*)notification {
[[self managedObjectContext]mergeChangesFromContextDidSaveNotification:notification];
}
But I've a problem : when I delete an element in the rootViewController, I really don't know how to manage the changes in the background process, because there is a loop inside that browse all the entities : if I delete one entitie when the background loop is at the same entitie, this is... really bad...
My solution was just to stop the update process when I delete an entitie, and then restart it, simply... But I've realised that the changes made in the "main" context were not apply in the new context I just create for the update.
So I ask you : Why the changes aren't apply in the new context ? If this the wrong way, how do you do this ? Using mergeChangesFromContext or something else ?
Thanks you in advance.
Sorry, my mistakes :
- First, my entities contained other entities, and because of a bad relationship, the entities that was contained in the parent entitie weren't deleted.
- Second, I was thinking that call -cancelAllOperations will stop the current operation, but it's not, you have to check in the nsoperation if the process is cancelled with [self isCancelled].
That's all !
U don't need to take care if somebody will delete something from interface, bcs NSArrayController is thread safe. But if u make updates and delete in same time, u have to take care about it.

reload uitableview simultaniously

i have to reload uitableview simultaniously using a thread. i'm already using two threads for loading data from web. Is it possible using a thread for reloading tableview? Is there any other way?
Simultaneous to what? The reloading takes time and you need to reload the backing data model in background, while still displaying being able to display data to the user?
If that is the case, then I would:
Define the data model as property.
Update a temporary data model in a background thread.
When updated I update the data model property on the main thread.
It is important to do the update of the real model property, and request of reloading data for the table view on the main thread. Otherwise there will be a time slot where the table view can request a view for a data model item that is no longer available.
An implementation would be something like this:
-(void)releadData;
{
[self performSelectorInBackground:#selector(reloadDataInBackground)
withObject:nil];
}
-(void)reloadDataInBackground;
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
MyDataModel* model = nil;
// Do what is needed to setup model.
[self performSelectorOnMainThread:#selector(updateModelOnMainThread:)
withObject:model
waitUntilDone:NO];
[pool release];
}
-(void) updateModelOnMainThread:(MyDataModel*)model;
{
self.model = model;
[self.tableView reloadData];
}
simultaneously with what?
everything UIKit has to be done on the main thread, so you can use background threads to talk to the internet or to do your own processing, but any actual interaction with a UITableView has to be on the main thread.