I have two entities and they have the exact same three attributes (name, desc, displayOrder), both have a handful of records, and my goal is to add/insert every item from "Entity 1" into "Entity 2".
Close, But No Cigar
I think my code is quite close. The console print out shows my code is successfully sending each item in "Entity 1" to "Entity 2" and saving it. BUT they save over each other! The first item is moved and saved, then the second item is moved but copies over the item that was previously moved. End result: only the last item moved actually shows up in the final "Entity 2".
Question: How do I fix this?
#IBAction func testOutMoveList(sender: AnyObject) {
//Setup 'Do Later' context
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let new_Ent2_Item = NSEntityDescription.insertNewObjectForEntityForName("Entity2", inManagedObjectContext: managedContext)
//Get sorted CoreData list and assign it to targetList_Cntxt
let fetchRequest = NSFetchRequest(entityName: "Entity2")
let sortDescriptor = NSSortDescriptor(key: "displayOrder", ascending: true)
fetchRequest.sortDescriptors = [ sortDescriptor ]
do {
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
if let results = fetchedResults {
entity2_Cntxt = results
}
} catch {
print(error)
}
for i in 0..<entity1.count {
//Grab a Today task item
let itemToMove = entity1_Cntxt[i]
//Assign the Today item's contents to variables
let nameToTransfer = itemToMove.valueForKey("name") as? String
let descToTransfer = itemToMove.valueForKey("desc") as? String
//Assign the Today item's contents to the target's 'task' object
new_Ent2_Item.setValue(nameToTransfer, forKey: "name")
new_Ent2_Item.setValue(descToTransfer, forKey: "desc")
//Insert the item!!
entity2_Cntxt.insert(new_Ent2_Item, atIndex: 0)
}
//Updates target list in Core Data after append, delete, and drag/drop
func update_TargetDisplayOrder() {
for i in 0..<entity2_Cntxt.count {
let item = entity2_Cntxt[i]
item.setValue( i, forKey: "displayOrder" )
}
}
Possible Clue: I have noticed that the displayOrder doesn't seem to be updating correctly. The first item should be 0, the second should be 1, etc but instead each time the code cycles the lowest displayOrder is 1 higher (A three item list might start with values 2,3,4 - then my code moves/copies another item to entity 2 and the display order values are: 3,4,5)
Question: What code can I add or fix to make this transfer I'm attempting work!?!
Bonus question: Post-transfer how do I quickly/easily clear all the values from "Entity 1"
The short answer is that you need to move this line:
let new_Ent2_Item = NSEntityDescription.insertNewObjectForEntityForName("Entity2", inManagedObjectContext: managedContext)
so that it is inside the for loop:
for i in 0..<entity1.count {
//Grab a Today task item
let itemToMove = entity1_Cntxt[i]
// Create the corresponding new Entity2 object:
let new_Ent2_Item = NSEntityDescription.insertNewObjectForEntityForName("Entity2", inManagedObjectContext: managedContext)
//Assign the Today item's contents to variables
let nameToTransfer = itemToMove.valueForKey("name") as? String
let descToTransfer = itemToMove.valueForKey("desc") as? String
//Assign the Today item's contents to the target's 'task' object
new_Ent2_Item.setValue(nameToTransfer, forKey: "name")
new_Ent2_Item.setValue(descToTransfer, forKey: "desc")
//Insert the item!!
entity2_Cntxt.insert(new_Ent2_Item, atIndex: 0)
}
To explain: the insertNewObjectForEntityForName is what actually creates the new Entity2 object. In its original position, that line is run only once, so only one Entity2 object is created. Your for loop then changes its attribute values and inserts it at the start of the entity2_Cntxt array (multiple times). Note that the last step in the for loop, entity2_Cntxt.insert(new_Ent2_Item, atIndex: 0) does not create a new object, or copy new_Ent2_Item, it just inserts it at the start of the array. With the amended code, a new Entity2 object will be created each time through the loop.
Regarding the displayOrder problem, if you inspect the entity2_Cntxt array each time through the loop, you should find (with your original code) that the array contains the same Entity2 object several times, followed by the objects obtained from the fetch. Suppose entity2_Cntxt contains the new object three times (at indexes 0,1,2). Your update_TargetDisplayOrder method will then set that object's displayOrder to 0, then 1, then 2. And then the objects returned by the fetch will have displayOrder 3, 4, etc. I think this should all come good when you move the insertNewObjectForEntityForName as above.
Related
I have an array of custom objects but when I add items to array it creates duplicate of last item add in array.
Below is my code, please suggest where is the mistake, this small thing not able to get it.
var tempArr:[AnimalViewModel] = [AnimalViewModel]()
do {
var objAnimal = Animal()
var result = try managedContext.fetch(fetchRequest)
for ds in result as! [NSManagedObject] {
objAnimal.name = (ds.value(forKey: "name")) as! String
objAnimal.type = (ds.value(forKey: “type”)) as! String
Var objAVM = AnimalViewModel(aniModel: objAnimal)
tempArr.append(objAVM)
}
} catch {
print(" Error ")
}
The array tempArr contains all duplicate element as last inserted element even objAnimal contains different values.
Thanks,
First of all never print a meaningless literal string like "Error" in a catch block. Print always the error instance.
Animal is obviously a class (reference type). You are creating one instance and the properties are updated in the loop. As always the same instance is used the values are overwritten and you get result.count items with the same contents.
Create new instances inside the loop and replace Entity with the real entity name
var tempArr = [AnimalViewModel]()
do {
let result = try managedContext.fetch(fetchRequest) as! [Entity] // let !
for ds in result {
let objAnimal = Animal() // let !
objAnimal.name = ds.name
objAnimal.type = ds.type
let objAVM = AnimalViewModel(aniModel: objAnimal) // let !
tempArr.append(objAVM)
}
} catch {
print(error)
}
And please notice and fix the warnings about never mutated variables
When I was inserting data to one entity of CoreData, All the rows are inserted successfully(Saved).
But when I try to fetch the data using FetchRequest, Only one row of data is coming even if number of rows inserted are 3 or 4 or anything(more than 1).
Remaining rows are not getting fetched. And when I print fetch results,
It says - Error
0:<EquipmentDetails: 0x6000000bad60>
(entity: EquipmentDetails; id: 0xd000000000040000
coredata:/EquipmentDetails/p1> **data:fault>)**
I didn't get what was going in backend of core data?
code for Insertion
func insertEqipToLocalDb()
{
let mobileNo : String = UserDefaults.standard.string(forKey: "phoneNumber")!
let equipDetailsItem = NSEntityDescription.insertNewObject(forEntityName: "EquipmentDetails", into:managedObjContext) as! EquipmentDetails
for (index,item) in array_IDEquip.enumerated()
{
equipDetailsItem.mobileNumber = mobileNo
equipDetailsItem.type = array_typeEquip[index]
equipDetailsItem.name = array_nameEquip[index]
equipDetailsItem.startDate = array_sDateEquip[index]
equipDetailsItem.endDate = array_eDateEquip[index]
equipDetailsItem.equpID = Int16(item)
equipDetailsItem.serviceDatesStr = array_serviceDateEquip[index]
}
do
{
try managedObjContext.save()
UserDefaults.standard.set("AlreadyInstalled", forKey: "statusInstallation")
}
catch
{
Exception.insertExceptionDetails(errorMsg: error as NSError, context: managedObjContext)
}
}
//code for fetching
let request = NSFetchRequest<NSFetchRequestResult>()
let entity = NSEntityDescription.entity(forEntityName:"EquipmentDetails", in: managedObjContext)
request.entity = entity
do
{
let fetchResults = try managedObjContext.fetch(request)
for r in fetchResults
{
typeEquipArray.append((r as AnyObject).value(forKey: "type") as! String)
}
}
catch let error as NSError
{
Exception.insertExceptionDetails(errorMsg: error, context: managedObjContext)
}
On this line:
let equipDetailsItem = NSEntityDescription.insertNewObject(forEntityName: "EquipmentDetails", into:managedObjContext) as! EquipmentDetails
You create one instance. In the loop that follows, you set values for the type, name, etc properties over and over again on that same instance. Then you save changes, which include just that one object. If you want a difference instance of EquipmentDetails for each pass through the loop, you need to create the instance inside the loop.
The "fault" message is not an error unless you tried to access the property values and found that they were not present. It's part of how Core Data works. See the answer that Harshal Valanda linked in the comments for more detail.
insertion code :
let dbObj: NSEntityDescription? = NSEntityDescription.entity(forEntityName: "CartTable", in: self.appDelegate.coreDataStack.managedObjectContext)
if dbObj != nil {
let myCartObj: CartTable = CartTable(entity: dbObj!, insertInto: self.appDelegate.coreDataStack.managedObjectContext)
myCartObj.menuId = self.itemListDataResponseModel?.menuItemId
myCartObj.menuName = self.itemListDataResponseModel?.menuItemName
myCartObj.menuPrice = self.itemListDataResponseModel?.price
//myCartObj.menuQuantity
//myCartObj.menuTotalPrice
myCartObj.userId = AppDataStoreManager.getUserDefault(KeyToReturnValye: "userId") as! String?
myCartObj.imageUrl = self.itemListDataResponseModel?.imgPath
myCartObj.desc = self.itemListDataResponseModel?.description
myCartObj.locationId = AppDataStoreManager.getUserDefault(KeyToReturnValye: "locationId") as! String?
myCartObj.locationName = AppDataStoreManager.getUserDefault(KeyToReturnValye: "locationName") as! String?
myCartObj.vendorId = self.vendorObject?.vendorId
myCartObj.vendorName = self.vendorObject?.vendorName
self.appDelegate.coreDataStack.saveContext()
deletion code :
self.appDelegate.coreDataStack.managedObjectContext.delete(obj)
self.appDelegate.coreDataStack.saveContext()
when i delete an item from cart table then all the related entries regarding to same menu id from carttablesidemenu item table must have to be deleted.
currently when i delete entry from carttable then it getting deleted but entries in carttable side menu not getting deleted that means relationship not working
NSManagedObject automatically creates its accessors methods so we have to use that methods to maintain relationships.
Setup: iOS 9, Swift, XCODE 7.1 Beta
Goal is to build shopping cart functionally for which I need unique values in CoreData.
I have a UITableView in which data (Product name, Cost, Quantity, ID) is uploaded from Parse backend. Each TableView custom cell has a button, tapping which saves the selected row data in CoreData.
I don't want to have duplicate product in the cart, so before saving I want to check if the cart already have the product. If it has, I want to replace the cart with currently selected data. If not, just want to add a new product in the cart.
My CoreData setup is simple.
Entity name = Cart
Attributes = pName(type String), pCost(type
Int), pQuantity(type Int), orderID(type String)
Add product to the Cart button code is as below:
// Product data is retrieved in these variable
var pNam = NSMutableArray()
var pCost = NSMutableArray()
var pQty = NSMutableArray()
var pObjectID = NSMutableArray()
// Add to Cart button ....
let indexPath = NSIndexPath(forRow: sender.tag, inSection: 0)
let cell = tableView.cellForRowAtIndexPath(indexPath) as! ProductMenuTableViewCell! // Custom cell in which the button add to Cart button is placed
let entityDescription = NSEntityDescription.entityForName("Cart", inManagedObjectContext: managedObjectContext!)
let cart = Cart(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext)
cart.pName = pNam[indexPath.row].description
cart.pCost = pCst[indexPath.row].integerValue!
cart.pQuantity = pQty[indexPath.row].integerValue!
cart.orderID = pObjectID[indexPath.row].description
var error: NSError?
do {
try managedObjectContext?.save()
} catch let error1 as NSError {
error = error1
}
if let err = error {
print(err.localizedFailureReason)
} else {
print("Saved")
}
Should I directly use cell to add values to CoreData? For ex: cart.pName = cell.pName.text! or there is a better way to do it? Anyone know how to solve this?
Your data setup is absurd. You have 4 arrays for the 4 attributes of n objects. If for any reason the sorting of an array changes, or an element is dropped or added, you have to make sure the same happens with all the other arrays, a maintenance nightmare! How do you expand this model if later you have 15 attributes. Use 15 arrays? This is completely crazy.
Instead, you should have an array of objects - ideally Core Data objects - with the appropriate attributes grouped together. You can always keep a flag to indicate that you want to discard these items later rather then persist them in the database.
Now you do not have to decide to create or update: simply set the delete flag to false.
You need to search for an existing Cart object with the correct name; if there is no matching Cart, then create a new one. Something like this:
var cart : Cart
let requiredName = pNam[indexPath.row].description
let entityDescription = NSEntityDescription.entityForName("Cart", inManagedObjectContext: managedObjectContext!)
let fetchRequest = NSFetchRequest()
fetchRequest.entity = entityDescription!
fetchRequest.predicate = NSPredicate(format:"pName == %#",requiredName)
do {
let results = try self.managedObjectContext?.executeFetchRequest(fetchRequest) as! [Cart]
if results.count == 0 { // not found, so create new...
cart = Cart(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext)
cart.pName = requiredName
} else { // found at least one, so use the first...
cart = results[0]
}
// Now update the other attribute values:
cart.pCost = pCst[indexPath.row].integerValue!
cart.pQuantity = pQty[indexPath.row].integerValue!
cart.orderID = pObjectID[indexPath.row].description
} catch {
print("Error fetching")
}
Regarding your secondary question, personally I would use your current approach: use the row as an index on your dataSource arrays, i.e. don't directly use the values from the cell's labels etc.
Let's say there are two favorite food lists: Johnny's List and Jane's List. They are both in CoreData, are separate entities, but have identical attributes. Both have four attributes: food name, secret ingredient, rating, and where to get it.
The user taps a button to merge the two lists. Specifically, Jane's list should now show up in Johnny's BUT her items should go first.
A very simple example set of lists:
Pre Merge - Johnny's List (only has 1 record)
Apple Pie -
Apples -
4.5 -
Susan's Diner
Pre Merge - Jane's List (has 2 records)
Turkey Dinner -
Turkey -
5.0 -
Mom's house -
BBQ Dinner -
Pulled Pork -
4.0 -
Route 6 Rib Shack
Post Merge - Johnny's List (has 3 records)
Turkey Dinner -
Turkey -
5.0 -
Mom's house
BBQ Dinner -
Pulled Pork -
4.0 -
Route 6 Rib Shack
Apple Pie -
Apples -
4.5 -
Susan's Diner
Johnny's new list of 3 items should save over Johnny's old list in CoreData. Jane's list should be cleared.
Solved!
There are likely several ways to solve this question but what I received for answers and found online weren't doing the trick. I took a shot at just writing the code and it works!
My Code
Here's my merge function.
It is passed only one parameter: the destination name. That is because my source list context was already setup outside the function.
Alternatively you could pass two parameters, the name of List A and List B, and set both contexts up in the function.
My CoreData fetch also has a sort predicate which you might not need.
func merge_ListA_Into_ListB (targetList: String) {
//Setup Targetlist context
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
//Get sorted CoreData list and assign it to targetList_Cntxt
let fetchRequest = NSFetchRequest(entityName: targetList)
let sortDescriptor = NSSortDescriptor(key: "displayOrder", ascending: true)
fetchRequest.sortDescriptors = [ sortDescriptor ]
do {
let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
if let results = fetchedResults {
targetList_Cntxt = results
}
} catch { print(error) }
for i in (0..<listA_Cntxt.count).reverse() {
//Grab a ListA task item
let itemToMove = listA_Cntxt[i]
//Assign the ListA item's contents to variables
let nameToTransfer = itemToMove.valueForKey("name") as? String
let descToTransfer = itemToMove.valueForKey("desc") as? String
//Assign the ListA item's contents to the target's object
let newItem = NSEntityDescription.insertNewObjectForEntityForName(targetList, inManagedObjectContext: managedContext)
newItem.setValue(nameToTransfer, forKey: "name")
newItem.setValue(descToTransfer, forKey: "desc")
//Insert the item!!
targetList_Cntxt.insert(newItem, atIndex: 0)
}
//Calls function that updates each records sort/display #
update_TargetDisplayOrder()
try! managedContext.save()
print("List merged into \(targetList).")
}
Good luck!