Core Data--pass name of entity or reference to entire entity? - ios

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.

Related

iOS Realm object by reference or duplicate objects

I am using realm and I am not sure about this.
I have two option to do in Post. One is to save entire owner object and another is to save only user object ID. If I save entire object, is it object by reference? I don't want to increase database size.
The reason why I want to store entire object is that it is easy to access. It is like Post.User.email. I don't need to query. I read through realm though. https://realm.io/docs/objc/latest/
Thanks in advance.
No need to go by ID. In Realm, when you add a property of another RealmObject type, that will store the reference, not the entire object. So it will not take more space on disk than what you are expecting.
I am not sure if your diagram reflects your actual types, but note that when using collections, you want them to be of type RealmList<>, not Array<> (I am assuming you are writing C# as you referred to the Xamarin docs).

Understanding of NSCoreData and MSManagedObject subclasses

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.

Core data and data integrity: read operations vs write operations. How to protect?

I have what I think is a simple task.
I have a method called [self getPerson] that makes a simple GET request from a web service for a Person that returns some JSON and then transforms the JSON into an NSManagedObject. checks for an existing identical Person NSManagedObject, and if none is found, saves the Person into core data. No problem.
However, If I fire off this method twice in a row, I get two Person NSMangedObjects persisted into Core Data. For example:
[self getPerson];
[self getPerson]; ---> yields duplicate `Person` objects saved in core data, no good.
How can I ensure that only one Person object is saved in Core Data (no duplicates allowed)?
I know the issue, I just don't know how to fix it. The issue is that I need a transaction. When [self getPerson] fires the first time, the method checks for an already existing identical Person object, finds none, and saves a new Person into core data. This is correct. When I fire [self getPerson] the second time, the method checks for an already existing Person object, doesn't see one, and is then persisting another Person object. This is not correct. I would like to make it so that the second time, and third time, and fourth time, to the 1000th time, checking for an existing Person object will only occur after the managedObjectContext saveoperation is done. Right now the check for an existing object is happening so fast (before the save is done).
Do I need a serial queue? If so, should this be a dispatch_async or dispatch_sync? I've even toyed with the idea of trying to use a performSelectorWithDelay trick.
Once you create the object it will exist in the database regardless of you calling save. So you should not create a managed object if one exists already. It's not entirely clear what your code logic is but from your description you say you transform the JSON to a managed object and then you check for an identical existing one and if none is found you save. Well when you create the managed object you have created it, so it's too late to check if an identical one exists. Saving does not create the object it just saves it to the store if it hasn't already been saved.
So first check if an person object exists with the attributes in the JSON and if not then create a managed object.
Well, in this case a serial queue will ensure that operations are performed in the correct manner.
From you question, maybe I'm missing something, I cannot understand if the getPerson method is responsible to both get and save data. If not, you should do it.
Anyway, if you use JSON and the person you retrieve form the server has a unique identifier, you should use that to query against Core Data and verify if it exists or not. The correct manner to do it is to implement Implementing Find-or-Create Efficiently.
A simple question. Is the any reason for calling the getPerson twice? Could you not prevent it using a flag (or a transient property)? Just simple ideas.

db4o object update dilemma

I am new to db4o.
I have this question in mind:
when the object are retrieved from DAL, maybe it will update in Business layer, then we lost it's original property, so when it comes to updating how can I find which one is the original object in the database to update?
You need to be more precise about "the object". If you modify the object instance's properties, simply storing it again will perform an update:
MyClass someInstance = ObjectContainer.Query<MyClass>().FirstOrDefault();
someInstance.Name = "NewName";
someInstance.PhoneNumber = 12132434;
ObjectContainer.Store(someInstance); // This is the update call
[This is just pseudo-code]
So you don't need to match objects to each other as you would have to when using an RDBMS.
However, you need to make sure you are not using a different instance of ObjectContainer, because a different container will not know these objects are the same instance (since there is no ID field in them).
Your application architecture should help to do this for most workflows, so there should be really only one IObjectContainer around. Only if timespans are really long (e.g. you need to store a reference to the object in a different database and process it somehow) it'd use the UUID. AS you already pointed out, that requires to store the ID somewhere else and hence complexifies your architecture.
If you however intend to create a new object and 'overwrite' the old object, things get somewhat more complicated because of other objects that might refer to it. However, this is a somehwat pathological case and should typically be handled within the domain model itself, e.g. by copying object data from one object to another.
You should load the object via its ID:
objectContainer.get().ext().getByID(id);
or via its UUID:
objectContainer.get().ext().getByUUID(uuId);
See the docs for the latter one. For an explanation see the answer here or the docs here. In short use uuid only for long term referencing.

Populating an Object from the DB -- Where do you stop?

When getting an object from the DB, should the object's properties also be loaded? There seems to be these approaches.
Create well-formed, fully-loaded objects.
Pro: No need to check if a property has been loaded; it has. Pass it around and don’t worry about parts of the object not being there.
Con: Where do you stop? If an object has an object, and that object has an object, and that object has 40 objects as properties, etc… Do you load the whole DB? Or do you make a decision in the BLL as to what constitutes a well-formed object, and load those properties?
Don’t load any properties that are other objects.
Pro: Quick, no loading unnecessary properties.
Con: Code has to be constantly written to check if properties are populated.
Lazy-loading: only load properties when they are first used.
Pro/Con: Not sure what to say about this approach. It seems intuitively wrong.
Is there another approach? What approach is the best?
And finally, what about properties that can be null? For example, a car may not have a PreviousOwner object. Do you set it to null? An empty PreviousOwner object? Does that property belong in another class then?
There's no easy answer to your question because it depends on what you're trying to achieve.
It looks like you expect a more or less complete object graph to be loaded from the database (i.e. with relationships between multiple object types and the objects themselves stored in the database).
If this is the case, I would look into using the Object Relationship Mapper that's convenient in my language of choice.
As to how much of the object graph is being loaded, the model employed by Apple CoreData's system is objects not yet retrieved are marked as faulty (they call the concept "faulting" - it's described in Limiting the Size of the Object Graph: Faulting. This is a play on the lazy loading concept you described yourself.

Resources