I am trying to understand the concept in saving and retrieving records in a one-to-many relationship entities in CoreData. i have two tables Products (Master) and Sales (Details) hence (one-to-many) relationship with the relationship key being (sales).
Issue 1: when registering a sale to a product. i create an instance of the Sales entity, populate the values, then add it to the product.sales (relationship) and attempt a managedObjectContext save.
First Question: Do i also have to enter the instance of the Sales entity into the Sales Entity? or that will be updated automatically since there is a relationship?
Second Question: If i want to query all the sales that happened to date, do i query the Sales Entity? or do i query the relationship in the Products Entity?
Thanks for your help!
tab images
First Question: Do i also have to enter the instance of the Sales entity into the Sales Entity? or that will be updated automatically since there is a relationship?
Something you might not realize here is that you are inherently saving the object by adding it to the managedObjectContext. As soon as you do something like
let sale = Sale(context: managedObjectContext)
followed by
managedObjectContext.save()
the context issues a save request to your persistent store (your actual SQL database).
Therefore your question whether you need to store the Sale as well is answered, it will always be stored upon saving the context.
Second Question: If i want to query all the sales that happened to date, do i query the Sales Entity? or do i query the relationship in the Products Entity?
That depends...
First let me give you a little tip/best practise:
Always make sure to set up an inverse relationship
In the Core Data Editor for your Product entity's relationships you can do something like this:
Your sales relationships look something like this:
A relationship is nothing more but a dependency between two entities, therefore there is always an inverse relationship between two entities, make sure you hook them up as shown above.
Why am I telling you this ? Remember I mentioned it depends what entity you do your query on ? This is where it matters.
For example, if you want the Sale for a given Product, you would query the product itself (by querying its relationship called sale):
let product = [A product instance from your Core Data store]
let sale = product.sale // returns the sale the product is associated to
If you want all the products from a given sale, you would query the Sale entity leveraging the products relationship:
let sale = [A sale from your Core Data store]
let products = sale.products // the products contained in the sale
You mentioned that you want all the sales to a given date:
It would not make any sense querying the Product entity for that because each product only has a relationship to the sale it is contained in.
So, to answer your question, you should query the Sale entity to retrieve all the sales to a given date.
I hope that helps, let me know if something is unclear.
let manageObjectContext = appDelegateObj.managedObjectContext
let entity = NSEntityDescription.entityForName("", inManagedObjectContext:manageObjectContext)
let manageObj = NSManagedObject(entity: entity!,insertIntoManagedObjectContext: manageObjectContext)
manageObj.setValue("Mark Developer", forKey: "AttributeName")
do {
try manageObjectContext.save()
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
I will need to look into this a bit further, as I too am still getting my head around Core Data, but until I can find the time to do that I think what might work for you is this:
issue 1: to also enter the sale into the sales entity. I do not believe this will be done for you. Where relationships really become important is in deleting. You can specify what you want he delete rules to be, whether nullify, deny, cascade, or no action. Raywenderlich.com has an excellent tutorial series on Core Data that may help you.
issue 2: I think it depends on in which table (although in CoreData the term is ManagedObject) the data you are looking for is stored. It would make most sense to me to query the sales entity. You don't have to query the whole sales entity if you do not want to. You can specify search parameters.
As of swift 3 you don't have to create the properties of a class of an entity that you created in Core Data because Core Data does that for you. You do have to initialize those properties though. Same thing is true for relationships. For example in the case of the product Class (the parent) the relationship to sales(the child) is already instantiated for you. So you can access and set the Entity Sale by getting the instance of the relationship in the Product Class. There might be other ways of doing that but I think using extensions is pretty cool.
extension Product {
//You would call this constructor like that: Product(sale) from any //part of your program as long as you "import CoreData"
//If its 1 Sale then s 1-1 if it's more than 1 it's 1-many
convenience init?(Sale or Sales) {
let MOC = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext
self.init(context: MOC)
//case 1 when sale is 1-many
let sale = Sale(context: MOC)
//case 2 when sale is many-many(this can be an array of objects that you sent)
let sale1 = Sale(context: MOC)
let sale2 = Sale(context: MOC)
let sale3 = Sale(context: MOC)
//setting sale object
sale.property = ""
//Now is time to set the relationship with the object that you just set
//case when is 1 - many
self.sale = sale
//case when is many - many
self.sale?.adding(sale1)
self.sale?.adding(sale2)
self.sale?.adding(sale3)
}
Now your Product will be related to your sales....
Related
Let say I have core data and three entity inside : Department, Employee, Inventory
So every department could have more employees, and every employee could have more items that is record as inventory.
Department <--->> Employee <---->> Inventory
Now lay say, that we have method (service,...) that return new list of employee for department.(Let assume that this could happened quickly.)
The logic is that we delete all instance of Employee of specific Department, and then insert new ones.
The best way would be (for me), that I could do something like that :
let employees_local = myDepartment.employees
if let employees = employees_local {
myDepartment.removeEmployees(employees)
}
But this (in my understanding) only remove relationship between those object and don't delete all those objects.
I know for solutions where you delete all entity of a kind (delete all Employees)
like : this post, or this one.
I even know that I can do a batch delete from ios 9 on. Like here
My question is, is there a faster/ better way to do this. Shouldn't be there a way, that you tell core data to delete all object that have no relationship on parent?
Using the example you looked at above linked as "this one".
If you use a predicate that filters the Employees by checking the relationship to Department is nil, that'd return just the data items you want. Then I suggest you could delete all of them.
I am working on a project where the user would be able to save an image, with a caption and title, and then have it saved using Core Data. The issue I am having is trying to add an album, where users could keep similar images, with captions. I am new to CoreData and have spent all day viewing documentation and combing through Stackoverflow.
I have attached an image (certainly not an artist) of the basic layout I am looking for.Layout
I have also attached an image of what I believe the data structure would look like. Data Structure
The parts tabbed with red are the issues I am having structuring. I have already structured what I believe would be the 'Top' layer of the data structure.
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext
let saveData = NSEntityDescription.insertNewObjectForEntityForName("ScannedItem", inManagedObjectContext: context)
let imageRep = UIImagePNGRepresentation(image)!
saveData.setValue(textTitle, forKey: "savedTitle")
saveData.setValue(scannedText, forKey: "savedText")
saveData.setValue(imageRep, forKey: "savedImage")
do{
try context.save()
}catch{
print("Failed")
}
How would I go about adding a lower level of data, one to hold a group of strings and one to hold a group of images?
I have been trying to structure it for hours and simply can not figure it out.
I hope the images can add context to what I am asking.
How would I go about adding a lower level of data, one to hold a group of strings and one to hold a group of images?
Don't have a group of strings and a separate group of images (how would you know which string belongs to which image?). A ScannedItem object already has both the string and the image, so you should group Scanned Item objects together to make an album.
In CoreData terms, you would create a new entity, Album. That entity might have its own attributes, such as a albumName or a albumDescription, plus a relationship to represent the ScannedItems in the album. Since an Album is a collection of many ScannedItems, the relationship would be "to-many".
In CoreData, if you define a relationship from one entity to another, you should always create the inverse relationship as well. The question to ask yourself is whether each ScannedItem can be in more than one Album? If so, the inverse relationship is also "to-many"; if not, the inverse is "to-one".
By default, CoreData represents the collection of objects in a to-many relationship as a Set. This has two consequences:
first, there is no implicit ordering of the objects in a set. If the order is important, you could either make the relationship "ordered" (in which case CoreData uses an ordered set), or you could specify a sort order (eg. in ascending alphabetical order of title) when you access the objects in the relationship. If the user can pick and choose the order in an Album, you might need to explicitly model the sort order by adding a sortOrder attribute. That's easy enough if the relationship from ScannedItem to Album is to-one, but if the relationship is to-many, the ScannedItem might be the first image in one Album but the last image in a different Album.
second, a set can only hold a given object once. So an Album cannot contain the same ScannedItem more than once.
The solution for each is the same: create a third entity, which you might call an AlbumScanReference, which has a sortOrder attribute, a to-one relationship to Album and a to-one relationship to ScannedItem. The inverse relationships would both be to-many: an Album could have many AlbumScanReferences and a ScannedItem could also have many AlbumScanReferences.
If myScan1 was the first item in album1 and seventh item in album2, you would create two AlbumScanReferences:
let reference1 = ....
reference1.scannedItem = myScan1
reference1.album = album1
reference1.sortOrder = 1
let reference2 = ....
reference2.scannedItem = myScan1
reference2.album = album2
reference2.sortOrder = 7
Likewise if myScan2 was the second item and the eighth item in album1, you would have two AlbumScanReferences:
let reference3 = ....
reference3.scannedItem = myScan2
reference3.album = album1
reference3.sortOrder = 2
let reference4 = ....
reference4.scannedItem = myScan2
reference4.album = album1
reference4.sortOrder = 8
I have two Core Data entities called PreviousWorkout, and ExerciseData. The PreviousWorkout entity holds the following attributes: date, workoutName, and exercises. exercises is supposed to be an array of ExerciseData. After making the class with Editor > Create NSManagedObject Subclass, I then did the same thing with the ExerciseData entity.
I'm unsure whether or not this is the correct way of storing data using Core Data. My goal is to have a PreviousWorkout which contains an array of ExerciseData as well as date and workoutName.
The way I'm creating it in code is by creating a managedbjectContext with:
let managedObjectContext = (UIApplication.sharedApplication().delegate as AppDelegate).managedObjectContext
Then I create a PreviousWorkout array with:
let newItem = NSEntityDescription.insertNewObjectForEntityForName("PreviousWorkout", inManagedObjectContext: managedObjectContext!) as PreviousWorkout
I add all my stuff (date and workoutName) to newItem, then I attempt to create an array of ExerciseData with:
var exerciseData = NSEntityDescription.insertNewObjectForEntityForName("ExerciseData", inManagedObjectContext: managedObjectContext!) as [ExerciseData]
The program crashes at this point. I realize that this last line is very wrong but I'm not sure how to create an array of ExerciseData. After creating the array I would like to append to it, and then set values for its attributes before setting the newItem.exercises equal to the ExerciseData object.
Is this the correct way of creating a Core Data object that contains an array? How do I create the array of [ExerciseData]?
Thanks in advance
An entity is essentially a table so you would store many Workouts in it and create a Workout entity, not just a PreviousWorkout entity. Then you should have an Exercise entity and store many exercises in it. Then to store an "array" in Core Data, you make a relationship between the two entities (probably a many to many relationship because each workout can have many exercises and each exercise can be a part of many workouts). Then to add exercises to a workout object, you just append them to a Workout objects exercises property which is an NSSet and it points at Exercise objects.
So now you have many workout objects and exercise objects, you can fetch the previous workout by using a fetch request and sorting all of your workouts by date or whichever property you want to sort by and taking the first result of the fetch request.
You create a new exercise like so:
let exerciseData = NSEntityDescription.insertNewObjectForEntityForName("Exercise", inManagedObjectContext: managedObjectContext!) as Exercise
I am working on a project where I am using core data, but bit confused how to use it corectly.
I have two entities students and college
students has one-to-one relationship to college. That is every student will have one college.
college has one-to-many relationship to student. that is college can have lot's of students.
However, in my app I am letting the user to add the student first without any college info. So I am keeping the relationship to college optional. In a 2nd view, I let the user update the college information of a student.
Now I am while adding a new student I am just updating the basic field related to student (firs name, last name, age, etc.). Now in 2nd view while updating college info I am not sure what is the correct way to do it considering the relationship.
The first view pass the student object to 2nd view and I am updating it as follows:
College* college = (College*)[NSEntityDescription insertNewObjectForEntityForName:#"College" inManagedObjectContext:self.managedObjectContext];
college.name = #"name"
college.address = #"address"
[college addStudentObject:self.student];
Am I doing it correctly?
what should be the correct way:
Adding the college object in 2nd view and connecting the entity by adding the student object in the student relation of a college?
Retrieving the student object first then getting the empty college object from the college relation. Then updating the college object with data and then save the context.
I am not sure if the 2nd option make any sense but in case of first option, if I add a new college, the new college get connected with the student but the old college remain in the database as redundant data.
In this scenario, I would probably create another entity, StudentEnrollment with one to one relationship to both student and college. It would probably make it easier to manage student's courses, grades, etc as well as transfers to another college.
I've got a iOS Core Data performance problem.
Let's assume that we have 2 classes: "Class_A" and "Class_B". Both of them have their own ids and one-to-one relations between each other.
Now let's assume that I'm downloading data from the web that allows me to create these classes (data contains id's of Classes A and B, and informations about their relations). For example:
"There will be instance of Class_A with id=1"
"There will be instance of Class_A with id=2"
"There will be instance of Class_A with id=3"
"There will be instance of Class_B with id=10"
"There will be instance of Class_B with id=11"
"There will be instance of Class_B with id=12"
"Class_A with id=1 will be connected with Class_B with id = 12"
"Class_A with id=2 will be connected with Class_B with id = 11"
"Class_A with id=3 will be connected with Class_B with id = 10"
Because all these informations can be obtained in random order (for example information about connections between X and Y classes can be downloaded before info about existance of classes X and Y), I've used another kind of entity: Relationship.
Relationship contains of 2 fields: class_A_id and class_b_id
Every time I recieve data about relationship between ClassA and ClassB, I create instance of Relationship entity with according properties values.
Than every certain period of time I'm iterating over all instances of "Relationship" entities, and trying to create proper relations like this:
Fetch all instances of "Relationship" entity
For every fetched Relationship fetch instance of ClassA and ClassB according to "class_A_id" and "class_B_id" ids stored in Relationsip
If both - instance of ClassA and ClassB exists - create relationship beetween them.
Remove instance Relationship from the CoreData
After implementing this algorithm I've figured out it works well when using with rather low quantities of objects in Core Data.
The problem is that i'm storing thousands of "relationship" entities (informations about them are downloaded first) while informations about existance of "ClassA" and "ClassB" are downloaded less frequently.
The result is that every time I'm trying to create relationships using "Relationship" entities I'm fetching thousands of objects that contains id's of classes that don't exists!
So my solution to this problem is to enhance first step of proposed algorithm:
Instead of fetching all relationships in the CoreData, fetch only these that contains id's of Classes that exists in the system.
In SQL it would probably looks something like this:
SELECT * from 'Relationship' where
(SELECT * from 'ClassA' where id == class_A_id).count == 1
AND
(SELECT * from 'ClassB' where id == class_B_id).count == 1
My question is - how to achieve query like this in CoreData? Use subqueries there? Is yes - how? :)
If I understood correctly, if you don't want to modify too much your model, you have two options:
First option: modify the request process to only ask for relationship about entities you already have, this can be done easily if you have not so much entities of type A,B. Otherwise implement some sort of 'delta', this will avoid to have thousands of relationship you don't need (unless you need it for other purpose).
Second option: when you download items, I suppose it is a background process, in this case I would do the check immediately within there, and not batch from time to time. For example, you are downloading relationship, check if you already have A or B and mark the relationship table as complete or incomplete (maybe adding two attributes aExists bExists). As soon as entity A came in, go and mark all those entity in relationship as aExists, the same goes for B coming in, this is a normal fetch request where class_A_id exists. Then the batch can easily scan and get only those entities marked as complete, and create relationship. You have to implement a little logic, but in my opinion things would get faster.
New relationship would be:
Relationships {
class_A_id,
class_B_id,
classAexists BOOL,
classBexists BOOL,
batchRelationshipCreated BOOL,
}
Well you can implement both options anyway :-)