I have an existing large app using plists to store its data. I store application state indicating current item selected, current user, and various other current selections in user defaults. This worked fine when the data was in plists. Now I'm refactoring to use Core Data instead of plists. I want to store a reference to an object instance as the currently viewed object. I know sqlite and Core Data have an ID for this object in the database, but I'm not sure what to store in user defaults. Three options come to mind:
Generate my own sequential ID, store that with the objects I want to remember as "current", store that ID in user defaults.
Try to use NSManagedObjectID and store in user defaults. I "think" I can convert it to a string as follows. Does this work?
NSString *stringID = [[[managedObject objectID] URIRepresentation] absoluteString];
I could create a current state entity in Core Data which is a singleton, only one row. This entity could have a to-one relationship to the current object I want to keep track of.
Suggestions appreciated for the approach with best design consideration.
Repeating from a comment, what are the drawbacks to approach #2? I've read conflicting accounts on the web. Some say the object ID is not consistent across migrations, yet Apple itself seems to suggest this as the solution.
In the Apple Class Reference for NSManagedObjectID, they discuss this very case and talk about saving the ID in user defaults. It says, "Object IDs can be transformed into a URI representation which can be archived and recreated later to refer back to a given object (using managedObjectIDForURIRepresentation: (NSPersistentStoreCoordinator) and objectWithID: (NSManagedObjectContext). For example, the last selected group in an application could be stored in the user defaults through the group object’s ID."
Does that mean the object ID URI representation always stays valid, even across migrations?
You should use your own unique identifier (ideally which will not server only for the purpose of this question) for your objects and use this identifier to retrieve the object from Core Data and store this identifier in the NSUserDefaults. So, yours 1. solution is correct, though it need not to be sequential.
The 3. solution would work also, but it is not very clean.
Related
I'm currently implementing persistentStoreEnsemble:globalIdentifiersForManagedObjects: delegate and have a difficulty to decide about what global identifier to provide.
My Core Data model has two entities: book and image.
For book objects, there is no issue, as I should probably return a UUID, which should be stored in a dedicated property of the book.
However, I'm not sure about the global identifier for an image. Note that the image and book entities have one-to-one relationship in the data model, with a 'cascade' delete rule, which means that once a book is deleted, the linked image is deleted as well. So an image cannot exist in the database without being linked to a book.
Based on the above description, I was wondering if should I return NSNull, UUID, or a hash code (calculated at runtime from the image data) as the global identifier for a given image object?
Thanks for any suggestion.
Just use a UUID, I would say. I guess the image is stored as external data on the entity, right? If that is the case, Ensembles will sync it up, and will automatically use a hash to ensure it is not uploaded twice if the image happens to be the same. So for your entity, just use a UUID like you would for the book entity.
I have a static table for settings where I want to pull some stuff from an entity in Core Data. The use case does not lend itself to a table of records as you usually see. Rather each row of the static table is really a field related to the user--as in a user profile. I have a feeling that in testing I may have created more than one record in the entity. I know there are programs that let you see the SQL lite database underneath, but my question assumes you do not have this tool and are relying just on Xcode.
My question is when you have more than one record in a Core Data entity/table, and you try to load data from the managed object context into a VC, one field into one element, what record is shown by default?
Related to this, if you don't know how many managed object or rows are in the database, is there anyway to specify which record you want since there are no auto ids as you would use in a traditional database?
The record that gets loaded from the fetch first. Depending on your sort that might be consistent or it might be random.
I'm making a simple bank account tracker, for self-instructional purposes. I'm using Core Data to store three entities, related as in the screenshot:
WMMGTransaction objects are simply stored as they are recorded, and extracted as needed to feed tableviews and detail views. This will be done via NSFetchedResultsController and a predicate. I'm using MagicalRecord to access Core Data, if that matters.
My question is this:
When I pass WMMGAccount data from one VC to another, such as when creating a new account, or when selecting one from a list (via delegation as a rule), does it matter if I pass a reference to the entire entity, or can I just use an NSString bearing the .name of the account and identify the account when required with a predicate and an NSFetchedResultsController? I guess this is a strategy question, and may generate discussion, rather than having a cut and dried answer, but I'm wrestling with it, so I thought I'd ask.
It sounds like you're asking if you should pass an object to the code that needs it, or if you should pass information that could be used to look up the same object again.
Unless you need to use the managed object on a different thread or queue, you should always pass the actual object. No sense re-fetching an object you already have. It's extra work and code complexity that (unless there are some unusual extenuating details you didn't mention) won't help in any way.
If you are needing to use the object on a different queue or thread, passing information that can be used to look it up is the correct approach. But in that case-- don't pass the value of one of the properties. Use the managed object ID.
Core Data won't force name values to be unique, while the object's managedObjectID is unique. It's also faster when retrieving the object, because you can use objectForID: or existingObjectForID: instead of performing a fetch.
I am learning a bit on NSCoreData and before introducing it some existing projects I have, I would like to validate my good understanding of the core principles.
From what I have understood, NSCoreData make it easier to manage local storage of object (+retrieval after that) by subclassing our Model class from NSManagedObject rather than from NSObject.
Right ?
I have a few questions then. Let's consider I am building a real estate application with as core model object the class Property that can represent an appartment, a house, and all related information. Currently it is managed in my app as a subclass of NSObject.
1) I retrieve the properties from the server through a search query, and have written a initWithJson : method to populate each instance.
Now if I subclass Property from NSManagedObject, I will create my instances by using
+(id)insertNewObjectForEntityForName:(NSString *)entityName
inManagedObjectContext:(NSManagedObjectContext *)context
and I will be still be able to add a populateWithJson: to my class to fill in the properties.
Then I will create a lot of Property instances in the current managedObjectContext, and if I do a save, they will be stored at the physical layer.
If I call again the same webservice, and retrieve the same JSON content, I will recreate the identical managed objects.
How to avoid redundancy with the [managedObjectContext save:&error] call and not to store physically several time the representation of a single real life property ?
2) Let's say I want to store physically only some properties, for instance only the one the user want to have as favorites.
[managedObjectContext save:&error] will save all created / modified / deleted managed objects from the context to the physical layer, and not only the one I want.
How to achieve that ?
Am I supposed to declare another context (managedObjectContext2), move the instance I want to store in that context, and do the save in that one ?
(I mean, I will have a context just to manipulate the object, create instances from the JSON and represents them in UI ... and a second one to actually do the storage)
Or am I supposed to stores all the objects, and add a isFavorite BOOL property , and then fetching using a predicate on that property ?
3) The app has a common navigation pattern : the UITableView lists Properties instance with the minimum information required, and going on a detail view call a webservice to request more information on a specific Property instance (images, full text description).
Is it a good practice for instance to call the webservice only if the property.fullDescription is nil, and then update the object and store it locally with all detailed information, and the next time only to fetch it locally with a predicate on the property.id ?
What about object that might be updated server-side after they have been created?
Thanks for your lights
1) Retrieve the server data into a temporary form (array of dictionaries?), then for each possible property in the array, check to see if you already have an object in Core Data that matches. If you do, either ignore it or update any changed attributes; if not, create a Property object.
2) Decide which things you want to persist in order to support your app's functions. There's no point in creating a managed object for something you don't want to save. Note, though, that Core Data supports sub-classes if you want both Property and FavoriteProperty.
3) Entirely up to your "business rules"…. How often do you need local data to be updated? The only technical consideration might be the guideline to not keep large files locally that can be re-created on demand.
I have a Session class which I want to hold and store app's current session information (like lastLocation, remoteSessionToken, ...)
I have default Core Data setup with a bunch of tables and the most obvious solution I see is to create one more table "session" with two string fields: 'field' and 'value' and store session information to this table in way like I store any information to other tables
'field' 'value'
lastLocation 46.68,34.18
removeSessionToken au987asdv7tta487tv9b
...
Also, I know I could have this done using a special separate file for settings fx in YAML or JSON format but this would lead to a logical inconsistency with having both Core Data database and a file.
Is there any recommended approach for holding and persisting session information?
Unless something like the following applies, put it in user defaults or in a special purpose file in the documents directory:
You have many sessions and need to search or filter them based on some kind of predicate
You have more than one session and sessions have some kind of relationship to specific objects in the data store.
It's not a "logical inconsistency" to use Core Data and separate files. Do what makes sense. It's not likely to make sense to keep this information in your data store, so don't do it unless you have some compelling reason.
If the information could be considered sensitive, put it in the keychain.
Also, it's extremely ugly to create a generic container entity in Core Data. Using field and value fields like you describe is not a good design decision.
As long as it's not sensitive information, I would be consistent and use Core Data. A more obscure place would be to put it in NSUserDefaults.