UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Core Data - One Entity has an array of another Entity

Forums > Swift

I have one Entity - Item - has a bunch of attributes (name, price, etc). Thousands of Items in the database. Multiple quantity of an item (Cokes - Quantity 50).

Creating another Entity - Sale Attributes - dateOfSale, tax, saleNumber, then an Array of the type Item (made from a custom class)

I'm thinking that a relationship wouldn't fit this case (because Sale wants an item, but item doesn't want a sale).

So in the xcdatamodel for Sale

Attribute - items // Type - Binary Data

In my custom class for Sale, how do I convert the array of Items into Binary Data?

Or is that not the way to go?

3      

I don't see why you can't do that with a relationship. I think you misinterpret the relationship as a must from both sides, but it isn't. Just because every Sale needs an Item but not every Item has a Sale doesn't mean you can't use it.

3      

Having a bit of trouble wrapping my head around this. I created the one to many relationship from Sale to Item.

The Entity Item is an entry into the inventory. So an Item (Coke)<Item # DR001> would have a quantity of 50. (There 50 of them on sale in the store).

The Entity Sale, besides the saleDate and saleNumber, has a list of Items purchased. My intent was to have an array (or NSSet) of type Item. But if they bought 2 Cokes, wouldn't an NSSet delete the duplicates. Sale doesn't have a quantity for each item, just the relationship I just created to Item. Item has quantity, but it's the total amount in inventory. If I change the quantity in Sale, would it change the quantity in Item?

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

No, internally CoreData creates a SQLite database with primary keys. So in your NSSet are unique items in the sense of the database. You can even add one Item more than once, if you wanted to. Basically, this works like any relational database you know. CoreData is just a layer above the database. Believe me, I use it this way and it does work :)

You can view the database with any SQLite viewer to get your head around it. :) Personally, I use https://apps.apple.com/us/app/core-data-lab/id1460684638?mt=12 but any other will do as well.

3      

I went this route, but it has the problem I thought would happen. In my CoreData I have items_ which is [NSSet]. I use this to covert to Set

var items: Set<Item> {
        get { (items_ as? Set<Item>) ?? [] }
        set { items_ = newValue as NSSet }
}

I created two test Items (item1 and item2). I created a new Sale, and added the saleDate and the saleNumber. Then I added item1 with

newSale.items.insert(item1)

Then I added 2 more items to the items. Both are item2, as they bought 2 of those.

newSale.items.insert(item2)
newSale.items.insert(item2)

Everything works, no errors.

But when I print off the sale I created, there are only 2 items in the items Set (one of item1 and one of item2). Sets and NSSets have unique items in them, they both delete duplicates.

3      

Does this also happen when you add them to the Sale object with the addTo... method? You get this method (and a bunch more) when you let Xcode create the entity objects. If you define relationships etc. and let Xcode create the entity objects you have a set in the entity object. F.e.

extension WorkoutDevice {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<WorkoutDevice> {
        return NSFetchRequest<WorkoutDevice>(entityName: "WorkoutDevice")
    }

    @NSManaged public var deviceName: String
    @NSManaged public var standardAlgorithm: String
    @NSManaged public var startingWeight: Int16
    @NSManaged public var settings: Set<WorkoutDeviceSetting>?
    @NSManaged public var workoutPlans: Set<WorkoutPlanToDevice>?
    @NSManaged public var workoutValues: Set<WorkoutValue>?

}

// MARK: Generated accessors for settings
extension WorkoutDevice {

    @objc(addSettingsObject:)
    @NSManaged public func addToSettings(_ value: WorkoutDeviceSetting)

    @objc(removeSettingsObject:)
    @NSManaged public func removeFromSettings(_ value: WorkoutDeviceSetting)

    @objc(addSettings:)
    @NSManaged public func addToSettings(_ values: NSSet)

    @objc(removeSettings:)
    @NSManaged public func removeFromSettings(_ values: NSSet)

}

I would go for the provided CoreData methods and variables and wouldn't create them myself.

Edit: For using CoreData you have to use the entity objects managed by CoreData. When you create your own classes this won't work.

3      

I just switched it to deal with the NSSet directly.

extension Sale {

    @objc(addItemsObject:)
    @NSManaged public func addToItems(_ value: Item)

    @objc(removeItemsObject:)
    @NSManaged public func removeFromItems(_ value: Item)

    @objc(addItems:)
    @NSManaged public func addToItems(_ values: NSSet)

    @objc(removeItems:)
    @NSManaged public func removeFromItems(_ values: NSSet)

}

Use this to run it:

    newSale.addToItems(item1)
    newSale.addToItems(item1)
    newSale.addToItems(item1)
    newSale.addToItems(item1)
    newSale.addToItems(item1)
    newSale.addToItems(item1)
    newSale.addToItems(item2)

When you print the sale, it has 1 of item1 and 1 of item2.

3      

Hm, I think it is because you add the same instance. I have never had a use case adding the same instance. Just for testing purposes, could you create two Item entities of the same and add them?

Is your relationship 1:m or m:n? However, I don't think you can add the same instance of an object twice. I'm sorry that I misunderstood your first post and wrote a lot of unhelpful garbage.

3      

I'm sorry I don't have a solution, but I think I see the problem. You are trying to place item1 into the set multiple times. A set's members must each be unique, therefore if you place item1 into the set, you can't put another item1 in the set. This would explain why you only see item1 and item2 in the sale when it's printed. Those are the unique items you placed into the set.

If I understand your question, you are going to need to use an array for you collection since it allows for duplicates.

3      

Now that we see what we can't do (which was a great learning experience, now I have a much better understanding of NSSets and Sets), I know now I can't use a relationship for this problem.

So how can you take a struct, create an array of that struct, and convert it to Binary Data (or is it Transformable) and save that in Core Data. I thought I once saw a video somewhere of the process, but I'll be damned if I can find it. I want to say that codable is involved somehow.

struct Entry {
   var itemNumber: String
   var quantity: Int
}

So the Sale Entity for Core Data would have

@NSManaged public var saleDate: Date?
@NSManaged public var stateOfMaineTax: Double
@NSManaged public var saleNumber_: Int32
@NSManaged public var entries: Data? // Or Transferable - Array of Entry

3      

@ShadowDES I think you may still be able to use the relationship, but you need to rethink how things are structured and possibly your model If you are putting Coke on sale, you don't put say 50 cokes on sale, you put Coke, so that item in your data would be on sale. If you are only wanting to put say 30 out of 50 of Coke's on sale, then you have to rethink how you model your data.

Quantity is a property of your Coke entity not your Sale entity. Maybe you could post a photo of your data model.

3      

I don't think my system is flawed. I have inventory, which has a bunch of items in it. So I have Coke as an item, and I have 50 of them in inventory.

Next I'm creating a sale. I could sell anything. So the customer buys 2 cokes. A sale needs to know how many cokes they bought. They didn't buy all 50, they just bought 2. So sale needs to know a quantity of an item (and the reason why I can't use an NSSet or Set).

The data is going to be an array of the struct Entry. It would be best to just save that into CoreData, if that is possible.

3      

You can't use a struct for this, you would have to use a class for a transformable object.

What I would do is the following. Transform the array of items of your Sale in a seperate Table=Entity -> SaleItems. Sale keeps the Sale unique properties and Item keeps its unique properties. In this new table store the reference(=relationship) to the Sale and the reference(=relationship) to the Item plus the amount you sold in an extra field.

In your Stock of Items you have every Item only once but with an amount property as well.

3      

Maybe I'm misunderstanding what you mean by sale... When you use the word sale do you mean the same thing as a purchase or a discount on an item?

If it's a purchase, you are basically creating a join table in databasey terms. A sale will contain multiple items and items will be in multipe sales, a many-to-many relationship. In the below example, SaleDetails joins the Items and Sales together.

So in Core Data, you will need three Entities

Items -> name, quantity, cost

SaleDetails -> identifier(could be a UUID), relationship to sale(to-one), relationship to items(to-many), quantity, total

Sale -> identifier(could be a UUID), saleitems(relationship to sale details, to-many), sales tax, subtotal, total

You'll have to create your own functions to process/update quantity of items left after subtracting the quantity in SaleDetails Initial State of inventory: Item Coke quantity = 50, Item Sprite quantity = 40

Sale 001 (contains multiple details)

  • SaleDetail 001 -> Item: Coke, quantity:3
  • SaleDetail 002-> Item: Sprite, quantity: 5

Process Sale 001 -> Inventory State: Item Coke quantity = 47, Item Sprite quantity = 35

Sale 002

  • SaleDetail 003 -> Item: Coke, quantity: 1
  • SaleDetail 004 -> Item: Sprite, quantity: 1

Process Sale 002 -> Inventory State: Item Coke quantity = 46, Item Sprite quantity = 34

3      

Transformation was the way to go, and I have it working. I'll post the code in the next day or so.

4      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.