CoreData Relationship Saving - iphone

I am having troubles with the relationship I have setup in CoreData. Its one to many, a table can have many people but only one person can sit at one table.
My app is based off Apples CoreDataBooks example here.
This does't handle relationships though. You see the 'EditingViewController.m' in the example project, I have this as well, its where the user edits and saves a change to a chosen attribute of a CoreData object.
So let's say they go and choose to edit the name attribute of my person entity. They get a text field where they enter a name and then tap the save button, done.
Now my person entity has a relationship with my table entity, but I can't get it saving correctly and it's likely that I don't fully understand how it works yet. So let's say they select to edit the table of that person, they get a UIPickerView howling a list of tables, great, I've gotten this far, I've used a fetch request to get the table objects and list them.
Normally setting a standard attribute, a line such as this would be called in my code:
[editedObject setValue:textField.text forKey:editedFieldKey];
However, this time I am using a relationship so this won't work, right?
So now my relationship is setup, I try this instead of the above:
[tableObject addGuestObject:(Person *)editedObject];
So from the fetch request that I filled my picker view with I work out the selected table and get that object, tableObject. Then as the CoreDataBooks example did for me already the editedObject has been passed down to this view and is the person in question that we are editing.
So this is where I am liking misunderstanding it. Surely as with the last line of code, we simply take the existing table object (that was selected) and then add the existing guest object to it?
But this gives me this error and crashes:
'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x145640 <x-coredata://2FAD62C1-382A-4398-A4CA-02B4B41DC9A5/Table/p2>''
Not entirely sure what to do to remedy this.

Generally the 'could not fulfil fault' means that the object was deleted out from the persistent store yet you still have a reference to the object. That could happen for a number of reasons (persistent store changed, object was deleted by another thread, object was owned by another object with a relationship which had cascade and it was deleted). Think we need a bit more info about the relationships from it.

Related

Setting relationships in core data swift after context saved

Working with a core data model and I am still new this. Right now I have a view controller that saves the relationship between 2 entities and it stays fine until I have to add another relationship into the primary core data entity. When I do this the relationship between the initial 2 relationships breaks and the tableview is no longer able to present the relationship and thus crashes. I know in order to set the relationship I need to insert it into the already existing context but how do I actually go about that without clearing out the previously set relationhip?
Let's say I have a entity named primary and one named secondary. The relationship between these two sets fine. When I try to set the relationship for the third entity the relationship between the secondary and primary clears out. The real problem is that I am actually passing the object I want to store the relationship into so when I do code similar to this:
passedObject?.relationhips = entity.relation
it isn't actually setting the relationship. Am I missing something? Do I have to insert something into the primary entity context outside of setting the relationship?
A really common core data crash is when you've updated your model but the app on the simulator has data that uses the old model and crashes. So after updating your modal it's a good habit to delete your app from the simulator before running again (although I think Apple made a change in iOS9 where your app will migrate the data automatically but I've not checked that).
If that's not the problem it would help a lot to see a screen grab of your model to see how relationships between your entities are set up.
Because you're new to core data, I'd check to see if the relationship type if 'to one' (the default) or 'to many' (to use if you want a connection between a set of entities instead of just one).
Always check core data subclasses after you have them created automatically by Core Data. Sometimes the setup is incorrect as was the case with my problem.

Core Data object graph design decision

I am designing an app which tracks data on Game objects. Each Game has a name, a date and other attributes. The problem I am having arises because I want the user to be able to add more names (for example) to pick from in the application. (in this case from a UITableView). So the user is presented with a list of names to choose from, and if the one they want is not in the list, they can add one to the list.
My solution is that I currently have a second entity called GameName so that I can show the user a list of those game names to pick from when they are adding a new Game. I just call an NSFetchRequest on all the GameName objects and display them in the UITableView. There doesn't have to be a Game object created yet to do this.
My dilemma is that I want to know if this is a good practice. It seems that if I do it this way, I will end up having a lot of entities with just one attribute, for the sake of allowing the user to pick from and add to a customizable list.
I hope this makes sense. I can clarify anything upon request.
Your approach is fine, and is commonly used in database design. The entity you want to add is called a "domain table" in databases. See this page, in particular this paragraph:
In a normalized data model, the reference domain is typically specified in a reference table. Following the previous example, a Gender reference table would have exactly two records, one per allowed value—excluding NULL. Reference tables are formally related to other tables in a database by the use of foreign keys.
Of course, you probably want to have an optional relationship between the GameName and Game entities.

How does deleteObject: work in coreData?

I just took the plunge and rewrote my App on top of CoreData (previously I was using my own internal save format).
Things are mostly working, although I'm a little confused by the behaviour of deleteObject:.
I have an object that is part of my graph, and when I delete it nothing seems to happen to the object. The object has relationships where some of them are "Cascade" and some are "Nullify". Every relationship to / from the object has an inverse relationship.
After I delete the object, the only thing that seems to change is that the "isDeleted" flag is set on my object. All of the relationships exist as they did before.
If I try to find the objects using a NSFetchRequest, it does not find the deleted objects. However, if I traverse my graph using the KVC relationships, the NSSet returned contains all of the objects including the deleted objects.
After I send the save: method to my ManagedObjectContext, then everything is as I expect.
When I do a deletion, do I need to manually nil out relationships I don't want or do I need to continuously save to keep my data sane? This seems very counter intuitive to me.
Is there anything that I can do to "commit" the deletion or at least make my object graph sane short of doing a save. It seems a little drastic to be doing a save every time I want to modify my graph.
Thanks,
Ron
p.s. Here is some of the behaviour that seems strange to me:
Before deleting the object, this is the "description" of the parent object which has a categoryObjs "to many" relationship:
categoryObjs = (
"0x613e1a0 <x-coredata://1A1AE9E7-66B1-4F4D-A7AB-07D4504CAE2C/TestCategory/p9>",
"0x613e1b0 <x-coredata://1A1AE9E7-66B1-4F4D-A7AB-07D4504CAE2C/TestCategory/p12>",
"0x613e190 <x-coredata://1A1AE9E7-66B1-4F4D-A7AB-07D4504CAE2C/TestCategory/p7>"
);
After deleting the "p12" object (the middle one above), the state of the relationship does not change when accessed through KVC. If I try to fetch the TestCategory entities, then only two are found.
After a "save:" the p12 object disappears:
categoryObjs = (
"0x613e1a0 <x-coredata://1A1AE9E7-66B1-4F4D-A7AB-07D4504CAE2C/TestCategory/p9>",
"0x613e190 <x-coredata://1A1AE9E7-66B1-4F4D-A7AB-07D4504CAE2C/TestCategory/p7>"
);
Every time you call save:, your Managed Object Context must go back to the store and actually write the changes. This is expensive. Therefore, deleteObject: merely marks the object as deleted, which will actually be applied the next time you save. (Remember that this helps out with undo functionality too, it's just going against the way you want to do things.)
According to the documentation, the isDeleted property just states whether the object is going to be deleted upon the next commit, and sets an isDeleted flag on the object. Additionally, deleteObject: will remove the receiver from the context if it was never committed.
For example (where Objects A and B are NSManagedObject instances):
Create Object A
Save MOC
Delete Object A
Object A has been marked for deletion but is not actually deleted until you perform step 2 again.
Contrast with this:
Create Object B
Delete Object B
Object B is gone, since it was never saved, there is no "marking for deletion". It's simply gone.
Edit:
I'm just curious, are you using an NSFetchedResultsController for your tableview's datasource? It's worth looking in to, if you haven't already.
I think Core Data want to minimize memory & IO usage while deleteObject:, and do all the heaver jobs, like write sqllite file, in save:. That could be the most time-efficient way.

"On duplicate key update" for core-data

I would like to know if there is some kind of similar functionality or way to preform an "on duplicate key update" function with core-data as there is with MySQL.
What I want to do is to save an object to the database every time a user presses a button. But if the button is already pressed I want to update the row with some new values instead of adding a new row.
The only way I currently know how to do this is to read the rows from the DB, see if the row exists and then update it.. otherwise add a new row. This seems kind of bad to do this way, am I wrong?
The easiest answer to this is to run a query against the Core Data context and get the object back if it exists. This is the most efficient and least error prone solution to the problem.
You do not need to create a separate NSManagedObjectContext and attempt to deal with merge policies, that is a very inefficient and dangerous way to try and resolve such a simple issue.
Core Data handles a lot of caching for you in the background. If you are attempting to retrieve an object that you just created there is a very high probability that it is still sitting in the cache so the response to your query will be nearly instantaneous.
Note
I just went back to both of those sample projects again to file a bug against them and noticed that they have been updated and finally removed the suggestion of creating a new context. They are now using the NSUndoManager where appropriate and a single context.
EDIT
Remember that the core data framework manages persistence of your object graph. It is not an interface to a sqlite database.
Worry about your object life cycle. When do instances get created? When are they destroyed? What makes your instances unique? Using Books as an example entity, a book has an ISBN which is a unique way of identifying a title, yet many copies of each title can exist. You have two choices in your Entity model, you can create separate instances for each copy of the title or have one instance with a count attribute.
The sample projects CoreDataBooks and iPhoneCoreDataRecipes use NSUndoManager to track state changes between views.

NSManagedObject for temporary use, how to switch between NSObject and NSManagedObject

Im using a Core Data model for my iPhone app. I have been looking for a way to instantiate or use an Entity outside the ManagedObjectContext. (This should not be done, I know, Im also more looking for a way to not do that, but get the benefits anyway).
My challenge is that I have a view where the user can search for "Persons", all the search results are parsed and put into a Person managedObject then displayed in a list.
If the user clicks a Person from the list, then and only then would I like the Person entity to be persisted to the store, however this requires me to delete all the other results so they don't get persisted along with the desired one. Also to the best of my knowledge, if the user decides to quite the app, the store is persisted, potentially with all current search results mixed in with real user data!
Is there some way I could have a TempPerson NSObject I could use for the search list? Without, however, me having to manually pull the 45 attributes from the temp object and manually set them on the managedObject!
Sort of like:
NSManagedObject aPersonCorrectlyReturnedFromTheStore = (NSManagedObject *)tempPersonOfJustTypeNSObject
I have seen example code from Apple where they build a temporary store to facilitate undo/redo and other stuff on an object that is not yet persisted. This I feel would be overkill in my situation. I just need to display search results until the user selects a Person to persist.
Hope it is clear what Im trying to do, feeling like my Core Data vocabulary isn't quite large enough yet:)
Thanks for any suggestions.
You could create each temporary person object as an NSDictionary or NSMutableDictionary. You can then create a new Person managed object and use the fact that NSManagedObject instances are KVC compliant and use setValuesForKeysWithDictionary:.
New managed objects that are inserted are not actually persisted until you send the managed object context a save: message.
Keep track of them in a collection (set or array) -- you are probably already doing this since you are presenting the search results somehow. Then, delete (deleteObject:) them all except for the one(s) that the user selects.
The deleted managed objects will never be stored.