Core Data - Cascade delete in relationship not working - ios

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.

Related

How to save one record for multiple department using core data in swift 3

I have two tables name Employee & Department, I have given relationship like following ways,
See bellow my Department table
& this is my Employee table
func saveEmpDetails(empId: String, andEmployeeName empName: String, andDeptId deptId: String, andDeptName departName: String, andProfile profileName: String, createdOn minutesAgo:Double , empQuote quote:String) {
// Reading AppDelegate
let appDelegate = UIApplication.shared.delegate as! AppDelegate
// Getting managed object context
let managedObjectContext = appDelegate.persistentContainer.viewContext
let empID:Int? = Int(empId)
let _predicate = NSPredicate(format: "empId=%#", empId)
let _fetchRecord: Employee? = isRecordExist(withEntiryName: "Employee", with: _predicate) as? Employee
if _fetchRecord != nil {
// Update Employee record as managed objext
}else {
// Inserting Employee record as managed objext
let _empInformation: Employee? = NSEntityDescription.insertNewObject(forEntityName: "Employee", into: managedObjectContext) as? Employee
_empInformation?.empId = Int32(empID!)
_empInformation?.empName = empName
_empInformation?.empProfile = profileName
_empInformation?.createdOn = Date().addingTimeInterval(-minutesAgo * 60) as NSDate
_empInformation?.empQuote = quote
let departmentRecord: Department? = NSEntityDescription.insertNewObject(forEntityName: "Department", into: managedObjectContext) as? Department
// departmentRecord?.departName = departName
// departmentRecord?.departId = Int32(deptId)!
// departmentRecord?.addToEmployees(_empInformation!)
let allDepartments = NSSet(array : ["10", "11"]) // Set<String>
departmentRecord?.addToEmployees(allDepartments)
departmentRecord?.addToEmployees(_empInformation!)
}
// Saving the employee details using save context
appDelegate.saveDBContext()
}
Now i want to save the one record for multiple departments, lets assume ABC employee work in iOS department & Android
My Question is : how can i save that ABCemployee record at one time. I have given my lot of efforts & did stack-overflow but didn't come across such a condition.
Can anyone help to complete my tutorial.
Change your employee relationship from 'to one' to 'to many' so that the relationship between the two entities becomes 'many to many'. Then your employee will have a Set of departments. Just look up many to many core data relationships, there's a lot of info about it out there.

Save json dictionary to core data Swift 3

I am able to get the last guest dictionary value in the json array saved into core data as a dictionary using the transformable key value however the other guest dictionary values are not saving. Guest is also it's on entity for now but I was hoping to save the guest as a dictionary since this task doesn't require complex relationships. I'm sure I'm looking through the json, the value type for reservation.guest = [AnyHashable: Any]?Any suggestions would be helpful here is my json response https://pastebin.com/J28myW66, thanks
Note: using Alamofire for the HTTP Request. Also haven't included my entire class here as this is the main part of it. Reservation and Guest are both NSManagedObject classes
let managedObjectContext = CoreDataManager.shared.persistentContainer.viewContext
let reservationEntityDescription = NSEntityDescription.entity(forEntityName: "Reservation", in: managedObjectContext)
let guestEntityDescription = NSEntityDescription.entity(forEntityName: "Guest", in: managedObjectContext)
let reservation = Reservation(entity: reservationEntityDescription!, insertInto: managedObjectContext)
let guest = Guest(entity: guestEntityDescription!, insertInto: managedObjectContext)
let url = "\(serverEndpoint)\(path)"
manager?.request(
url
).responseJSON { responseData in
if(responseData.result.error != nil) {
print(responseData.response)
}
else if responseData.result.value != nil{
let json = JSON(responseData.result.value!)
let content = json["data"]
var reservationArray: [String] = []
if let dates = content.array {
for item in dates {
if let str = item["date_time"].string {
reservationArray.append(str)
print(reservationArray)
}
}
}
for (key,obj) in content {
let guestData = obj["guest"]
let guestDict = guestData.dictionaryObject!
reservation.guest = guestDict
reservation.id = obj["id"].stringValue
reservation.dateTime = obj["date_time"].date
reservation.startTime = obj["start_time"].time
reservation.numOfPeople = obj["number_of_people"].intValue as NSNumber?
reservation.status = obj["status"].stringValue
reservation.tables = obj["tables"].arrayObject as! [NSString]?
reservation.reservationCollections = reservationArray as [NSString]?
guest.id = guestData["id"].stringValue
guest.email = guestData["email"].stringValue
guest.name = guestData["full_name"].stringValue
guest.phone = guestData["phone"].stringValue
guest.notes = guestData["notes"].stringValue
}
print("Reservation to be saved\(reservation)")
print("Guest to be saved: \(guest)")
}
}
do {
try reservation.managedObjectContext?.save()
} catch let error as NSError {
fatalError(error.localizedDescription)
}
do {
try guest.managedObjectContext?.save()
} catch let error as NSError {
fatalError(error.localizedDescription)
}
When your code starts, you create one instance of Guest and one instance of Reservation:
let reservation = Reservation(entity: reservationEntityDescription!, insertInto: managedObjectContext)
let guest = Guest(entity: guestEntityDescription!, insertInto: managedObjectContext)
After that you never create any other instances. In your loop you assign values to this instance:
reservation.guest = guestDict
reservation.id = obj["id"].stringValue
...
guest.id = guestData["id"].stringValue
guest.email = guestData["email"].stringValue
...
But since there's only one instance, only the last pass through the loop gets saved. The first time through the loop you assign values to guest and reservation. Every other time, you overwrite the previous values with new ones.
If you want to save a new instance for every pass through the loop, you need to create new instances every time. Move the let guest = ... and let reservation = ... lines inside the loop.
Firstly you need to make design flow bit generic i.e The HTTP request/response, DataBase Part, Model Part and UI part.
Now create a generic model class for your response,
so that the values will bind in single object.
In you core data sub class your table i.e custom NSManagedObject Class.
First convert the dictionary objects [objects in content.array] into respective model class objects.
In that SBPlayer is a model class
Favourite+CoreDataProperties.swift & Favourite+CoreDataClass.swift are custom NSManagedObject class (auto-generated).
Now with every object, you have mapping respective properties in database table and in custom NSManagedObject class.
Map the values and Save it DataBase.
For example: https://github.com/Abhishek9634/ScoreBoard/blob/master/ScoreBoard/ScoreBoard/SBDBManager.swift
Reference : https://github.com/Abhishek9634/ScoreBoard

FetchRequst issue with data fault

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.

Add and Delete CoreData based on attribute value search match

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.

CoreData Object Move Between Entities Not Working

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.

Resources