So, I'm trying to use Core Data to save instances of a "Screenshot" class, which consist of:
NSString *note;
NSData *screenshot;
NSData *thumbnailOfScreenshot;
NSTimeInterval date;
In my Core Data file, i have two entities because i want to fetch the large images only when necessary, and only 1 at a time (it is to be used in a UITableView).
Entity 1 is called Screenshot and consists of the 4 attributes above, whereas the *screenshot is transient. This entity also has a to one relationship with entity 2(an int called index).
Entity 2 only has 1 attribute which is a Binary data field for the Large images. Also a to one relationship with entity 1.
So, my first question: Is this remotely close to being correct? I'm a little unsure if i even need the Transient attribute "screenshot", in entity 1. And i am also confused if i need to create a new class for entity 2, which seems abit odd, since it will just contain the images.
For now, I'm only trying to add and load instances to and from the DB.
Here is how i (think) i add an instance to the DB, but i am not sure it actually puts it in the DB right away?:
Screenshot *s = [NSEntityDescription insertNewObjectForEntityForName:#"Screenshot" inManagedObjectContext:context];
I am in over my head, so any help will be appreciated. I have read several guides, but none was targeting something similar to this.
Transient properties are not saved to Core Data (they are neither retrieved nor saved out from the backing store). They are most often useful for calculating some sort of property based on stored properties (e.g. I use them for queries based on the first letter of a person's last name -- I'm storing the full last name, but the firstLetterOfLastName is a transient property -- but one that I can order results on since Core Data knows about it).
If I'm understanding your model correctly, you likely want no transient properties at all.
I also don't think you need an entirely new model for your "large images". You can instruct Core Data to optimize it's backing store for large binary attributes by turning on "Store in External Record File". You'll find this in the Data Model Inspector pane underneath where Transient and Optional are set. This will keep the actual backing DB snappy, but allow Core Data to retrieve these large binary attributes and you may still work with them seemlessly with a Core Data NSManagedObject instance.
Also, in general, do not relate items by an "index" value of some other stored Core Data object. There's plenty of reasons to do so, but in general, this is what Core Data relationships are for. If "Movie" has multiple "Screenshots", for e.g, you would have a relationship on a Movie instance called "screenshots" that you simply add each screenshot to (it's effectively an NSMutableSet as far as you care).
Core Data is an object graph, not a relational database. If you add the same object instance to two relationships on different owner-object instances, you aren't duplicating the child-object -- Core Data does all the work to interrelate them for you (assuming your models are setup in a good way).
I'd rethink this. I think a single entity is enough for this. I'd either add a BOOL value named 'large' and do a fetch when "large = NO" to get the small images. Alternatively, if you want more control, add a 'pixels' or 'megapixels' field which describes the size of the image. Then you can fetch where 'pixels > 3000000' for instance.
If you have a special relationship between two images (i.e. one image is a thumbnail of another), I'd just add a relationship to another Screenshot entity.
Alternatively, you could also make one Entity the "Parent Entity" of the other (see the Data Model inspector when you have an entity selected).
You're on the right track. Keep thinking about how you want to use your entities. This should help you describe the relationships better. Quite often I write the code that uses the entities before I define them in a data model. This lets me end up with cleaner interfaces and less overlap between entities.
Related
I have two different entities in different stores of core data that get populated from two different sources in the app. There is specific column which is common in both entities.
For a specific functionality in the application, I need to combine the data. In SQL terms, it would be a join on that one column and then a certain query on the joined table.
I can't create a relationship between them since they are different stores and I can't move those entities in one store since that would break the logical separation I want.
What is the most correct best practice that is followed in such cases?
You can create a wrapper type that contains both of these entities like following -
struct CombinedEntity {
let entityA: EntityA
let entityB: EntityB
}
From the call site, you can instantiate this type when you have both of these records available in memory. There is no other responsibility of this wrapper type than to hold these two together.
This should be no problem, because Core Data has always been designed to load multiple persistent store files into the same Core Data "stack". That's why there was always a class called NSPersistentStoreCoordinator-- because it coordinated persistent stores. With one of those you can call addPersistentStore as many times as you like with different stores.
This continues with the newer NSPersistentContainer. It's no accident that the function you use is pluralized-- loadPersistentStores, with an s on the end, because you can load more than one. You do this by creating a different NSPersistentStoreDescription for each persistent store file and adding them to the container's persistentStoreDescriptions array. It will go ahead and load them all, and then any managed object contexts will fetch data from all of them.
One difference to be aware of is that with multiple stores, the completion handler that loadPersistentStores uses will be called multiple times, once for each persistent store.
If the different persistent stores also have different data models, Core Data can handle that too, with a little bit more setup. You can merge multiple data models into a single NSManagedObjectModel instance. It has initializers and convenience methods to create merged models. Depending on what seems more convenient, you can use either mergedModel(from bundles:) or init?(byMerging models:?). Either one combines multiple data models into one. Then use the merged model object as the value of the managedObjectModel parameter when you create the container.
I'm struggling with creating a suitable Core Data model for my app. I'm hoping someone here can provide some guidance.
I have two entities -- "Goals" and "Items". The Goals entity contains only a goal description, but any goal may have any number of subgoals, and these may extend multiple levels in a tree structure. Subgoals are to be contained within the same entity, so presumably the Goal entity will contain a pointer to "parent" which will be the parent goal of any subgoal.
There will also be an "Items" entity that contains a couple of text fields and a couple of binary items, and must be linked (ideally, by a unique identifier, perhaps objectID) to the particular goal or subgoal the item(s) are related to.
I am totally fumbling with how to set this model up. I know what attributes need to be in each entity, but the relationships, particularly between goals and "subgoals", has me stumped. I don't seem to be able to turn up any good examples of tree structures in Core Data on the Internet, and even the couple of books I have on Core Data don't seem to address it.
Can anyone here help an old SQL programmer get headed the right direction with these relationships in Core Data? Thanks.
Have you tried creating a one-to-many from Goal to itself, and a one-to-one from Goal to Item? The only thing I would worry about here is circular references.
Also, read Relationships and Fetched Properties in the CoreData Programming Guide.
Here is how it is done:
You set up a to-many relationship from Goal to Item in the model editor. Don't use any ids, foreign keys etc. This is old-fashioned database thinking - you can forget about it. Here we are only dealing with an object graph. The database layer is just an implementation detail for persisting the data.
Make two more relationships in entity Goal to itself: a to-one called parent, a to-many called subGoals. Make them the inverse of each other. Simple!
QED is correct, you can create a to many relationship on goal (call it subgoals) as well as a to-one relationship on goal (call it parentGoal) and set them as inverses to each other.
Then create another to many relationship (call it items) on the goal entity, with the inverse being a to one relationship on the item entity (call it goal). Then you're all set. You don't need to link items with a unique id, just add them to the items relationship.
Also note that if you did want to give items a unique id, do not use the objectID. The objectID should only be used as a temporary id as they are not guaranteed to remain the same. In fact they will change if you ever do a Core Data migration.
One way, though not really great, is to create a another entity, say subGoal, and each goal has one subGoal and each object of subGoal has many goal.
I do have some JSON file http://jsonblob.com/530664b3e4b0237f7f82bdfa I am pulling from forecast.io.
I am little confused how I should be creating my CoreData entities and relationships.
In below setup, I made my Location entity as the parent entity and created a separate entity for Currently, Minutely, Hourly, Daily. However I have decided it's best to hold all the information regarding the weather data in one entity, so I created a Data table for that purpose and tied it to Daily and Currently in the image below.
Before going further, I paused and would like to get a second opinion on it. Is this a valid way of going forward with this?
EDIT: Based on Wain's response I changed my model to this
Currently Minutely and Hourly add little value as they don't have any attributes or relationships. It's also generally easier to add a type attribute rather than having a number of sub entities because you can easily filter the type using a predicate while doing a fetch. If you're going to add more in the future then there could be a case for keeping sub entities.
Once the entities are trimmed down then you only have a Location and Data with a relationship. You should make that relationship bi-directional so that Core Data can manage the data store contents better. (this applies to all relationships, even if you keep the sub entities you already have).
Other than that, fine :-)
Im about to add the persistence layer to my application, and i decided to give core data a go. Currently i map all my models to entities, which seems to work quite well. But in my current implementation i use something i call "collections" (of models) for example i have a collection of tile slots in a game.
this SlotsCollection class has methods like findNextInSameRow() findAvailableSlot() etc. What ive done with core data is i have created a Game entity and added a to many relationship to the Slot entity, Is there a way to define a class which the collection of slots should be instantiated with so i can put my logic inside that? Or is there a better way for me to structure things. I guess i could create "managers" inside my Game entity and hand in the slots when initialized
SlotManager* manager = [SlotManager alloc] initWithSlots:self.slots];
Slot* slot = [manager findAvailableSlot];
Also after i "migrated" all my models to entities, i have alot of entities that do not have any attributes but only hold references to other entities. Im abit afraid im using a wrong mindset when structuring the core data.
The class that has the collection should have the logic for that collection.
If you have a 1-to-many relationship from A to B, then you'd put the logic about this relationship into class A — and possibly some of it inside class B (depending on your needs).
Note: If you're iterating through relationships, you need to be aware of faulting behavior etc. Whenever Core Data has to do actual database work, you incur a performance hit. That's no different that plain old SQL. If you don't have to "go to disk" things are very fast. If you're using fetch request you will always do database work, and things will always be (relatively) expensive.
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.