would like to ask for the community's suggestions.
I need a persistent store in my iOS app.
I have considered Core Data and SQLite and both are not ideal for the following reasons.
Core Data:
unable to model certain object relationships, such as Object A has a one-to-many relationship with NSString.
//Added:
unable to model a Dictionary(or map) as an attribute for a one-to-one relationship. i.e. Object A has a one-to-one relationship to Object B (and Object B behaves like a dictionary)
SQLite:
poor interface for schema management
no clean and elegant solution for data migration between schema versions
//Added:
unable to model a Dictionary(or map) as an attribute for a one-to-one relationship. i.e. Object A has a one-to-one relationship to Object B (and Object B behaves like a dictionary)
the persistent store has to be able to support search. If anyone can suggest ways to circumvent the problems, that would help too.
Based on your limited description, Core Data would work just fine. Core Data can easily do one to many relationships. No matter what technology that you use, you still need table to table (SQLite) or Object to Object (Core Data) relationships. Nothing is going to give you an Object to String relationship.
You can do Object A to Object B where Object B has only one property which happens to be a string.
Related
I'm new to Core Data and I'm trying to implement it into my existing project. Here is my model:
Now, there's some things that don't make sense to me, likely because I haven't modelled it correctly.
CMAJournal is my top level object with an ordered set of CMAEntry objects and an ordered set of CMAUserDefine objects.
Here's my problem:
Each CMAUserDefine object has an ordered set of objects. For example, the "Baits" CMAUserDefine will have an ordered set of CMABait objects, the "Species" CMAUserDefine will have an ordered set of CMASpecies objects, etc.
Each CMAEntry object has attributes like baitUsed, fishSpecies, etc. that point to an object in the respective CMAUserDefine object. This is so if changes are made, each CMAEntry that references that object is also changed.
Now, from what I've read I should have inverses for each of my relationships. This doesn't make sense in my model. For example, I could have 5 CMAEntry objects whose baitUsed property points to the same CMABait object. Which CMAEntry does the CMABait's entry property point to if there are 5 CMAEntry objects that reference that CMABait? I don't think it should point to anything.
What I want is for all CMAUserDefine objects (i.e. all CMABait, CMASpecies, CMALocation, etc. objects) to be stored in the CMAJournal userDefines set, and have those objects be referenced in each CMAEntry.
I originally had this working great with NSArchiving, but the archive file size was MASSIVE. I mean, 18+ MB for 16 or so entries (which included about 20 images). And from what I've read, Core Data is something I should learn anyway.
So I'm wondering, is my model wrong? Did I take the wrong approach? Is there a more efficient way of using NSArchiver that will better fit my needs?
I hope that makes sense. Please let me know if I need to explain it better.
Thanks!
E: What lead me to this question is getting a bunch of "Dangling reference to an invalid object." = "" errors when trying to save.
A. Some Basics
Core Data needs a inverse relationship to model the relationship. To make a long story short:
In an object graph as modeled by Core Data a reference semantically points from the source object to a destination object. Therefore you use a single reference as CMASpecies's fishSpecies to model a to-one relationship and a collection as NSSet to model a to-many relationship. You do not care about the type of the inverse relationship. In many cases you do not have one at all.
In a relational data base relationships are modeled differently: If you have a 1:N (one-to-many) relationship the relationship is stored on the destination side. The reason for this is, that in a rDB every entity has a fixed size and therefore cannot reference a variable number of destinations. If you have a many-to-many relationship (N:M), a additional table is needed.
As you can see, in an object graph the types of relationships are to-one and to-many only depending on the source, while in rDB the types of relationships are one-to-one, one-to-many, many-to-many depending on both source and destination.
To select the right kind of rDB modeling Core Data wants to know the type of the inverse relationship.
Type Object graph Inverse | rDB
1:1 to-one id to-one id | source or destination attribute
1:N collection to-one id | destination attribute
N:M collection collection | additional table with two attributes
B. To your Q
In your case, if a CMAEntry object refers exactly one CMASpecies object, but a CMASpecies object can be referred by many CMAEntry objects, this simply means that the inverse relationship is a to-many relationship.
Yes, it is strange for a OOP developer to have such inverse relationships. For a SQL developer, it is the usual case. Developing an ORM (object relational mapper) this is one of the problems. (I know that, because I'm doing that for Objective-Cloud right now. But I did if different, more the OOP's point of view.) Every solution is a kind of unusual for one side. Somebody called ORM the "vietnam of software development".
To have a more simple example: Modeling a sports league you will find yourself having a entity Match with the properties homeTeam and guestTeam. You want to have an inverse relationship, no not homeMatches and guestMatches, but simply matches. This is obviously no inverse. Simply add inverse relationship, if Core Data wants and don't care about it.
After learning about relationships between entities in Core Data. I don't see the real reason for setting up relationship between two entities. They can be connected separately if one of the entities contains a property that can hold another entity by having a property of type NSManagedObject.
#property (nonatomic, strong ) NSManagedObject *AssetType;
This is a concept you must understand: Core Data is not a database but it is an object graph manager and, as a second functionality, offers persistence (e.g using for example a Sqlite store).
Said this, if you have two separated entities and you need to retrieve values based on the conditions that belong to the other entity, you need to run two requests and filter the results in memory. On the contrary if you set up a relationship you can just create a request wih a specific predicate and let Core Data to retrieve the correct results for you. In addition, through relationships you can access objects that belong to another entity as simple as accessing a property object. For example, the following snippet says that based on entityA I can access a property calles someRelationship that allows to retrieve one (or more) entities of type EntityB. If someRelationship has been set up as to-many you'll receive one or more EntityB entities.
entityB = entityA.someRelationship;
The real advice is to think in terms of objects graph!!!
Further reference: Core Data Overview by objc.io.
Update 1
The other big advantage is that relationships allow you to take advantage of deletion rules and, through inverse relationships, you are able to maintain the integrity of your graph.
See Relationships and Fetched Properties.
By example:
I have an entity User, and an entity Device.
User have a To Many relationship toward Device, called devices.
Device has the inverse of this relationship called user.
Now I collect, persist a bulk of devices from e.g. a network service, hydrate them into an NSSet, then I bound them to a particular user, so I do:
NSSet *collectedDevices = [API getSomeDevices];
someUser.devices = collectedDevices;
Will Core Data populate the inverse user relationship for each Device for me? Does it observe the setters for relationships?
Background:
I'm aware of the Core Data setters for setting collections, but I want to avoid using them. I'm actually reconstructing Core Data entities from JSON representations with KVC without hardcoded attributes, relationships, just enumerating their entity descriptions, and set matching values.
Yes, Core Data will set the inverse relationships whether you use properties or KVC or the Core Data specific methods such as -setPrimitiveValue:forKey:.
However, when it sets that inverse can be slightly variable. It can set it immediately in some situations and in others it may wait until the end of the run loop to set the inverse. As long as all of the objects being related are created against the same NSManagedObjectContext then the referential integrity will be maintained by Core Data.
I have a bunch of code that sets Core Data relationships via the inverse instead of the "addWhateverObject" methods. This usually works well in all of my applications, but I have one app where the relationships are being lost when the NSManagedObject context is being saved.
My question is setting a relationship in Core Data via the relationship's inverse valid or do you have to use the accessor methods to add objects to the relationship?
Thanks!
Yes, using the inverse is valid. Core data does all of the appropriate updating for you when you do that. See the comments on this question Core Data To-Many Relationship Creating Duplicates When Adding Object to Parent Entity
I have a MySQL database and would like to have a similar structure in Core Data. I am very new with using Core Data with Xcode. I have a few fundamental questions if I am doing the right thing.
My Mysql DB looks similar to this:
table.caveconditions
visibilityID
percolationID
xxxx
table.visibility
visibilityID
visibilityValue
...and so on. I would then connect the tables using JOINS
Now, I have done the Core Data modeling like this but I am not quite sure if this is the right approach.
Would be great if someone of you could tell me if this is the right way to do it. In the end I would like to use JSON strings to dump the mysql table into core data.
Thanks a lot
Chris
I have created the new schema. Is this right?
It looks good except for all the "xxxID" attributes e.g. caveID. You also need to follow the naming conventions.
You have the same attribute names with (presumably) the same values in two or more entities. This is necessary in SQL for joins but in Core Data, this is handled by objects and relationships.
Each object in Core Data is automatically universally unique. This means when you create a relationship from one object to another, that relationship concrete identifies on specific unique object.
This means you only need an attribute like caveID in the actual entity that caveID designates which in this case is (presumably) the Caves entity. You don't need the attribute in the CavesConditions entity or any other entity that has a relationship to the "Caves" entity.
(If the xxxID were just artifacts of SQL, you don't actually need them at in Core Data unless some external database your app interacts with requires them.)
A good rule of thumb to use is that any particular value should show up on only one side of a relationship and, ideally, only once in the entire data model.
Naming conventions are a little different than SQL. A Core Data entity isn't a table. An entity is more akin to a class. Each entity is supposed to describe a single instance of a managed object. How many of those instances end up in the object graph is irrelevant. Therefore, entity names are singular.
In this case, Caves should be Cave, Countries should be Country and so on.
Relationships are named after the entity they target. It is not immediate obvious but each reciprocal relationship (the default) on the visual data model editor is actually two relationships because there is one relationship description for each side. Each side has the name of the entity targeted. By convention to-one relationships have a singular name and a to-many relationship has a plural name.
So:
Caves.relConditions<-->>CaveConditons.getCave
...would become
Cave.conditons<-->>CaveConditon.cave
The naming conventions are important because Objective-C uses conventions names to generate and search for accessor methods.
CoreData is NOT a database. Remodel your data as simply as you can and in a way that suits how it will be used in your application and do not think about joins or structure based optimization. You do not have control over the backing schema of a CoreData object model. This is the hardest concept you must get over when starting to use CoreData, but once you do, you will be better off.