Reputation: 280
I'm trying to build a cart with core data I have created 3 objects to model my data Product, Quantity, and CartItem which holds a product and a quantity and I save them as a transformable object in core data.
I have created a class for my xcdatamodeld that have a CartItem attribute
@objc(CartItemContainer)
class CartItemContainer: NSManagedObject {
@NSManaged var cartItemAttribute: CartItem
}
I'm saving to core data with no problems, but whenever I try to update the quantity using the code below it doesn't change it would still be 1.
static func changeQuantity(product: Product) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<CartItemContainer>(entityName: "CartItemContainer")
var results: [CartItemContainer] = []
do {
results = try context.fetch(fetchRequest)
if let productToChangeQuantityOn = results.first(where: {$0.cartItemAttribute.product.productID == product.productID}) {
let oldQuantity = productToChangeQuantityOn.cartItemAttribute.quantity.value
productToChangeQuantityOn.cartItemAttribute.quantity.value = oldQuantity + 1
try context.save()
context.refresh(productToChangeQuantityOn, mergeChanges: false)
}
}
catch {
print("error executing fetch request: \(error)")
}
}
I have tried updating it without calling context.refresh(productToChangeQuantityOn, mergeChanges: false)
it would change the quantity at run time, but when the app is relaunched the quantity would still be 1.
What am I missing here?
Any kind of help will be appreciated.
Update: Here is how I set up Product for example. Quantity, and CartItem have the same set up.
class Product: NSObject, NSCoding, Decodable {
let productID: String
let category: String
let images: Dictionary<String, String>
let name: Dictionary<String, String>
let price: Dictionary<String, String>
func encode(with aCoder: NSCoder) {
aCoder.encode(productID, forKey: "productID")
aCoder.encode(category, forKey: "category")
aCoder.encode(images, forKey: "images")
aCoder.encode(name, forKey: "name")
aCoder.encode(price, forKey: "price")
}
required init?(coder aDecoder: NSCoder) {
self.productID = aDecoder.decodeObject(forKey: "productID") as! String
self.category = aDecoder.decodeObject(forKey:"category") as! String
self.images = aDecoder.decodeObject(forKey: "images") as! Dictionary<String, String>
self.name = aDecoder.decodeObject(forKey: "name") as! Dictionary<String, String>
self.price = aDecoder.decodeObject(forKey: "price") as! Dictionary<String, String>
}
}
Upvotes: 1
Views: 682
Reputation: 5020
Welcome to Core Data, Ahmed. I'm happy you got it working. Before you go, I'd like to suggest a more conventional data model which will probably work out better for you in the long run. No transformables are needed.
Broadly, the reasons are given in the first page of Apple's Core Data Programming Guide states 11 features of Core Data, listed as bullet points. By using transformable attributes instead of relationships, I'd say you are only fully realizing the advantages of the first and fifth bullet points.
Regarding your particular design, I assume that the same product can be in many Carts. By giving CartItem
a product attribute of type transformable, this same product must be somehow reproduced in each cart. If you want to change the attributes of a product, in your design, you must find all the carts with this product in it and change each one. With the conventional design, you just change the Product object. Your design requires more code (which is always bad), and your users' devices will use more resources and be slower executing your code.
Last but not least, you don't want other engineers to be scratching their heads when they look at your code or data model :) And you want to be able learn from Apple documentation, tutorials, blog posts, stackoverflow answers and sample code you find on the internet. These resources are not as applicable if you are doing things in a non-conventional way.
Upvotes: 2
Reputation: 280
Transformable attributes are a immutable type, therefore cannot be changed. The only way of changing them is by creating a new object and saving it again to core data.
To solve this I deleted the property cartItemAttribute from my xcdatamodel which holds both a product and a quantity as one transformable attribute. I replaced it with a product attribute of type transformable and a quantity attribute of type Int and everything works fine now.
Here is my updated code
static func changeQuantity(product: Product) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<CartItemContainer>(entityName: "CartItemContainer")
do {
let results = try context.fetch(fetchRequest)
if let productToChangeQuantityOn = results.first(where: {$0.product.productID == product.productID}) {
let oldQuantity = productToChangeQuantityOn.quantity
productToChangeQuantityOn.quantity = oldQuantity + 1
try context.save()
context.refresh(productToChangeQuantityOn, mergeChanges: false)
}
}
catch {
print("error executing fetch request: \(error)")
}
}
Upvotes: 0