What I have is a Core Data Entity called "MyDocument" which has these properties
fileName
fileExtension
fileURL
I download a bunch of files from the server, save them on the disk in the "Caches" Folder and then Insert rows in the DB for each document. This just makes it easier to manage documents in the app without listing directory contents etc...
Everything seems OK, except that when I delete the Entity, I also want to delete the associated file on disk. I could easily do something like this
for(MyDocument *myDocument in ParentEntity.mydocuments)
{
[[NSFileManager defaultManager] removeItemAtURL:[NSURL fileURLWithPath:myDocument.fileURL] error:nil];
[context deleteObject:myDocument];
}
But I am trying to get this done via accessors....so that I can call - deleteObject:myDocument from anywhere and be sure that the associated file would also get deleted.
I know I could use Core Data's External File Storage option and not worry about this at all but I am using QLPreviewController to preview these documents, and QLPreviewController needs a file URL to be able to preview the item. And if I save the documents in Core Data, I would have to write the file to disk from the stored NSData every time Preview needs it. It did not make sense so I decided to store them externally myself and keep a reference in DB.
So, how would I write a custom accessor that would jump in just before object is about to be deleted and delete the associated file and then carry on with deleting the actual Entity..
Thanks in advance
NSManagedObject -prepareForDeletion is most certainly what you need to implement in your entity, to take care of associated resources.
Core Data calls prepareForDeletion for every deleted entity while still alive and well and before the delete rules propagation. This is the right place to implement anything more complex than the very basic rules Core Data provides.
It works without adding stuff to the NSManagedObjectContext, it will work with the default NSManagedObjectContext -deleteObject, and it will not mess with the NSUndoManager. Of course, you have to use custom classes for your entities.
I think the cleanest way is to simply add a custom method to your NSManagedObject subclass. Below I made this a category of NSManagedObjectContext, but you could also do it just as a MyDocument instance method. In this way you can explicitly delete the Entity and associated document while still having the option to just delete the entity. Also, you would avoid deleting things accidentally in the future when you not that familiar with your code any more ;-).
#interface NSManagedObjectContext (customDelete)
-(void)deleteMyDocumentObjectAndAssociatedFiles:(MyDocument *)object;
#end
#implementation NSManagedObjectContext (customDelete)
-(void)deleteMyDocumentObjectAndAssociatedFiles:(MyDocument *)object {
[[NSFileManager defaultManager] removeItemAtURL:
[NSURL fileURLWithPath:object.fileURL] error:nil];
[self deleteObject:object];
}
#end
Or as MyDocument method (don't know if this "self deletion" works):
-(void)deleteSelfAndAssociatedFiles {
[[NSFileManager defaultManager] removeItemAtURL:
[NSURL fileURLWithPath:self.fileURL] error:nil];
[self.managedObjectContext deleteObject:self];
}
Related
Archiving is easy, now concepts of CoreData are confusing me.
What's the connection between the declared NSManagedObjectModel and my actual xcdatamodel file?
I declared NSManagedObjectModel, NSManagedObjectContext and accordingly a NSPersistentStoreCoordinator, and connected all three of them. During this process, I didn't see any clue that the declared NSManagedObjectModel property had anything to do with my xcdatamodel file.
Only when I initiated a NSFetchRequest, I told it the name of my targeted entity, and which was my NSManagedObjectContext.
And that'll do it? I don't have to tell anyone the name of my xcdatamodel file, but only the name of the targeted entity? Does the NSFetchRequest have to search all my xcdatamodel files for the certain entity? What if I have two entities of same name in two different xcdatamodel?
When you build your app, the .xcdatamodel file is compiled to produce a .mom file, which is incorporated into the app bundle. The prefix remains unchanged so, "myApplication.xcdatamodel" is compiled to "myApplication.mom".
If you get the URL for this file in the bundle (for example using NSBundle's URLForResource:withExtension method) you can initialise your NSManagedObjectModel instance using initWithContentsOfURL. Alternatively, and this may explain the apparent absence of any connection, the NSManagedObjectModel instance can be created using mergedModelFromBundles class method, which uses ALL the models (.mom files) in the given bundle.
The NSManagedObjectModel is the object that represents the entities you create in the data model (the .xcdatamodeld). The data model editor is sort of like a source code editor, while the NSManagedObjectModel is the thing you have that represents the compiled model.
The NSManagedObjectModel contains references to all of the entities in the model, which are represented as instances of NSEntityDescription. The purpose of the model object is
So that Core Data can understand the persistent store file. The model tells Core Data what entities to expect.
So that Core Data operations like fetch requests can be configured to properly access the persistent store file-- only using entities that actually exist there, for example.
So that instances of NSManagedObject can be created to reflect entities from the data model.
My app allows a user to edit data, but during save there are two things that can happen:
If the name of the data stayed the same, just save the object as an edited version. I.e. they are just editing the existing object.
However, if they have changed the name, this should create a new instance and restore the edited data to the original.
Obviously, 1 is the easy case and is working just fine. But I'm conflicted about the best method to handle 2. How best is it to save a modified NSManagedObject as a new row in the DB?
There is no obvious way to just "copy" a NSManagedObject. The most robust way is to simply recreate everything from scratch.
Make sure you have all the changed attributes stored separately (here I am assuming they are in various text fields or that they are unchanged from the existing object). You can make this decision (new instance or not) when your editing view controller is dismissed:
if (![nameTextField.text isEqualToString:object.name]) {
ObjectClass *newObject = [NSEntityDescription
insertNewObjectForEntityForName:#"ObjectClass"
inManagedObjectContext:self.managedObjectContext];
newObject.name = nameTextField.text;
newObject.attribute1 = oldObject.attribute1;
// or
newObject.attribute1 = attribute1TextField.text;
// do this for all attributes
[self.managedObjectContext save:nil];
}
I'm new to Cocoa and xCode, but not programming though.
I have created some some core data and a interface in the interface builder.
Now i need to edit and get some core data from my code. In fact I need to be able to get an "imagepath" to show a picture and to set a new value in the "imagepath".
"imapepath" is a core data attribute.
I have figured out how to insert a new entry, but i want to edit values instead.
NSManagedObjectContext *context = [self managedObjectContext];
NSManagedObject *places = [NSEntityDescription
insertNewObjectForEntityForName:#"Place"
inManagedObjectContext:context];
[places setValue:[tvarNSOpenPanelObj filename] forKey:#"imagepath"];
I hope you guys have some clues ;-)
If you have loaded your NSManagedObject from CoreData, then you can edit it's values just like any other object. This is stored in your NSManagedObjectContext (ie. just in memory).
You then need to persist this to your backing store at some point, so you need to call save: on your NSManagedObjectContext - and voila, it's saved.
You should read the programming guide for core data:
http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/coredata/cdProgrammingGuide.html#//apple_ref/doc/uid/TP30001200-SW1
(If you don't know how to load an object from Core Data, read the section on 'Fetching Managed Objects', and then 'Using Managed Objects' to know how to edit them).... in fact, read it all from the beginning to the end. Its invaluable for knowing how to use CoreData correctly and effectively.
I have an application that utilizes Core Data for data persistence. My application is comprised of (4) Navigation Controllers inside a TabBarController. The Navigation Controller's root view controller is a UITableView Controller and when you select a cell it displays a Detail view controller in each case. I wanted the main application to be static so the user cannot edit the data and my update would be made using a separate application that I would use to update the data and then put out a new version.
I have completed the data applications and it successfully saves the data and I can call it back up to display and all appears well. I have also looked at the data with a SQlite Data browser to make certain it is all good.
I then take the populated .sqlit file and place it in the documents directory of the main application and make sure the names match see below:
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"RoundTopApp.sqlite"];
After doing this and then running the application, I get the following error:
'+entityForName: could not locate an NSManagedObjectModel for entity name 'Place''
I have added the code below to note that I am using the NSFetchedResultsController to perform the fetch.
(void)viewDidLoad
{
[super viewDidLoad];
NSError *error;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
self.title = #"Lodging";
}
I am not sure what is wrong here other than I have failed to add it to the application bundle or something.
Any help would be appreciated.
Hudson
The error says that you are trying to access entities called "Place" from your data store, but that the Managed Object Model that you've defined in the app doesn't contain this entity. Perhaps you've added this entity in your other app where you're creating the data store? If so, you need to update the Managed Object Model in the app that is reading the data store to match the model in the app that has created the store.
Alternatively, if you've copied the Managed Object Model between the two apps, and you've only just copied this across, you probably need to do a Clean Build (Product > Clean). Ensure also that your new model is set as the current model, if you're maintaining a versioned model.
I've the following problem:
I have two or more persistent stores. And I have created an entity in the xcdatamodel named "House". Now I have these two files for the NSManagedObject House.
Now I want to know how do I save an instance of the entity house in a specific persistent store?
So I tried to work with [NSEntityDescription insertNewObjectForName:#"House" inManagedObjectContext:context] and [context assignObject: toPersistentStore:]. But it didn't worked until now. Am I on the right way to do it?
Can somebody give me a hint?
Answering my own question:
The problem was that I allocated a completely new persistentStoreCoordinate who coordinates all the stores. So the coordinator wasn't linked to the managedObjectContext.
Could be solved with
__persistentStoreCoordinator = [__managedObjectContext persistentStoreCoordinator];