Save json dictionary to core data Swift 3 - ios

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

Related

Is it allowed to pass NSManagedObject through performBlock of the same NSManagedContext?

Can I set the relationship between objects in DIFFERENT perform blocks of the SAME NSManagedContext?
I created a private context
and used (just example):
let pContext = persistentContainer.newBackgroundContext()
pContext.perform {
let user = pContext.fetch(<fetch Request>).first
pContext.performAndWait {
let book = pContext.fetch(<another fetch request>).first
book.name = "skjkjd"
pContext.save()
book.author = user
pContext.save()
}
}
May this code produce following error and in which cases?
Fatal Exception: NSInvalidArgumentException
Illegal attempt to establish a relationship 'author' between objects in different contexts
If I'm not mistaken but the AppDelegate persistentContainer.viewContext is a singleton. You shouldn't pass the MOC across threads the way you are trying to do.
Take a look at the apple documentation: https://developer.apple.com/documentation/coredata/using_core_data_in_the_background
TRY (not tested):
let pContext = persistentContainer.newBackgroundContext()
pContext.perform {
let user = pContext.fetch(<fetch Request>).first
let userObjcID = user.objectID
pContext.performAndWait {
let book = pContext.fetch(<another fetch request>).first
book.name = "skjkjd"
// pContext.save()
book.author = pContext.object(with: userObjcID)
pContext.save()
}
}

IOS - How to Insert data into different tables with relationship In Core Data Swift

We have two tables with one to one relationship. personel.idpersonel maps to personeltochart.idpersonel.
personel
personeltochart
On login event, we sync data of personel table from a webservice, and save it into personel tables. We have to get other table data on another event and save it.
The question is when we try to fetch data from these two tables why we get data of personel table correctly but data from personeltochart table is nil?
personel
firstname
idpersonel
personelToChart
id
idpersonel
Here is an example of saving data of table:
let jsonEmza = try jsonDecoder.decode([JSONNameTabel].self, from: data)
var privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateContext = CoreDataManager.shared.persistentContainer.viewContext
jsonEmza.forEach({ (jsonCompany) in
let tabel = jsonCompany.tableName
if tabel == "Personel"{
jsonCompany.personel?.forEach({ (field) in
let personel = Personel(context: privateContext)
personel.firstname = field.firstName
personel.idpersonel = field.id
})
do {
try privateContext.save()
} catch let saveErr {
print("Failed to save personel:", saveErr)
}
}
fetching example
let context = CoreDataManager.shared.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<Personel>(entityName: "Personel")
let predicate = NSPredicate(format: "idpersonel == %#", "147")
fetchRequest.predicate = predicate
do {
let listNameh = try context.fetch(fetchRequest)
listNameh.forEach({ (item) in
print(item.idpersonel ?? "")
print(item.lastname ?? "")
print(item.firstname ?? "")
print(item.personelToChart?.id)
})
} catch let fetchErr {
print("Failed to fetch Personel:", fetchErr)
}
The data of item.personelToChart?.id is nil.
Why is this happening?
It's like Sam said, you need assign an object to PersonelToChart on Personel. For example, here's my core data model
Leg (flightId, flightName, Aircraft)
Aircraft (aircraftId, aircraftName)
Make sure your relationship in Flight entity looks like
let Aircraft = AircraftCoreData(context: self.persistentContainer.viewContext)
secondAircraft.aircraftId = 02334
secondAircraft.aircraftName = "ABC"
leg.aircraft = Aircraft
You'll see that we created aircraft object, assigned values to its properties then assigned the object to leg.aircraft property. Now when you fetch the data, you won't get nil

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.

Core Data - Cascade delete in relationship not working

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.

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.

Resources