One to many relationship CoreData Swift - ios

Core Data works great for the most part. When I click on name first VC (Items) and performSeque to the second VC (Costs), I can see the costsName and other data. But when I add second name in first VC I can see the same data as in first name.
I'm trying to make a one to many relationship.
I have 2 data models:
import Foundation
import CoreData
#objc(Items)
class Items: NSManagedObject {
#NSManaged var count: NSNumber
#NSManaged var name: String
#NSManaged var cost: NSSet
}
import Foundation
import CoreData
#objc(Costs)
class Costs: NSManagedObject {
#NSManaged var costsDate: NSDate
#NSManaged var costsName: String
#NSManaged var costsValue: NSNumber
#NSManaged var account: Items
}
Here is addAccount's (name of the first VC) save action:
#IBAction func saveButtonPressed(sender: UIBarButtonItem) {
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
var managedObjectContext = appDelegate.managedObjectContext
let entityDescription = NSEntityDescription.entityForName("Items", inManagedObjectContext: managedObjectContext!)
let account = Items(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext!)
account.name = cellOneNameTextField.text
if cellTwoCountTextField.text.isEmpty {
} else {
account.count = (cellTwoCountTextField.text).toInt()!
}
// Saving data
appDelegate.saveContext()
var request = NSFetchRequest(entityName: "Items")
var error:NSError? = nil
var results:NSArray = managedObjectContext!.executeFetchRequest(request, error: &error)!
self.navigationController?.popViewControllerAnimated(true)
}
Here is addCost's save action:
#IBAction func saveButtonTapped(sender: UIBarButtonItem) {
// CoreData Access
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
var managedObjectContext = appDelegate.managedObjectContext
let entityDescription = NSEntityDescription.entityForName("Costs", inManagedObjectContext: managedObjectContext!)
let cost = Costs(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext!)
cost.costsName = cellThreeNoteTextField.text
cost.costsValue = (cellOnePriceTextField.text).toInt()!
cost.costsDate = datePicker.date
// Saving data
appDelegate.saveContext()
var request = NSFetchRequest(entityName: "Costs")
var error:NSError? = nil
var results:NSArray = managedObjectContext!.executeFetchRequest(request, error: &error)!
for res in results {
println(res)
}
delegate?.refreshTable()
self.navigationController?.popViewControllerAnimated(true)
}

I don't know if you do it somewhere, but your Items should attach a Count object to itself using your cost variable from your Items. Something like :
let account = Items(...)
let cost = Cost(...)
account.cost.addObject(cost)//and changing your var cost:NSSet into var cost:NSMutableSet
//then save Items
(I haven't tried the addObject but you understand the principle)

Related

Fatal error: unexpectedly found nil while unwrapping an Optional value inside an entity

I want an UICollectionViewCell to be deleted when the delete button is tapped:
#IBAction func deleteButtonClicked() {
error here: delegate?.deleteTrigger(clothes!)
}
clothes:
var clothes: Clothes? {
didSet {
updateUI()
}
}
func deleteTrigger:
func deleteTrigger(clothes: Clothes){
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext!
let en = NSEntityDescription.entityForName("Category", inManagedObjectContext: context)
if let entity = NSEntityDescription.entityForName("Category", inManagedObjectContext: context) {
let indexPath = NSIndexPath()
//var cat : Category = clothing as! Category
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext: NSManagedObjectContext = appDelegate.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName: "Clothes")
let predicate = NSPredicate(format: "category == %#", self.selectedCategory!)
fetchRequest.predicate = predicate
var error: NSError? = nil
var clothesArray = managedContext.executeFetchRequest(fetchRequest, error: &error)!
managedContext.deleteObject(clothesArray[indexPath.row] as! NSManagedObject)
clothesArray.removeAtIndex(indexPath.row)
self.collectionView?.deleteItemsAtIndexPaths([indexPath])
if (!managedContext.save(&error)) {
abort()
}
}
Clothes is an entity in Core Data. Does anyone know why I am getting this error? I am trying to delete a collectionViewCell from core data with in a one-to-many relationship. Category is the parent entity and Clothes is the entity within the Category.
You have declared a property clothes:
var clothes: Clothes?
You never gave it any value, so it is nil. Thus when you force-unwrap it by saying clothes!, you crash.
As others said, your value is nil. You need to unwrap the variable before you can do anything with it. Try this:
#IBAction func deleteButtonClicked()
{
if var clothes = clothes
{
delegate?.deleteTrigger(clothes)
}
}

How to correctly map one-to-many relationship using core data in Swift? Getting NSInvalidArgumentException error during attempts

Here's my medicine object class and extension:
import Foundation
import CoreData
class Medicine: NSManagedObject {
#NSManaged var alarms: NSSet
}
-
import Foundation
import CoreData
extension Medicine {
#NSManaged var name: String?
#NSManaged var dosage: String?
#NSManaged var type: String?
#NSManaged var image: NSData?
func addAlarmObject(value:Alarm) {
let items = self.mutableSetValueForKey("alarms")
items.addObject(value)
}
func removeDeleteObject(value:Alarm) {
let items = self.mutableSetValueForKey("alarms")
items.removeObject(value)
}
}
My alarm object:
import Foundation
import CoreData
extension Alarm {
#NSManaged var time: String?
#NSManaged var weekdays: String?
#NSManaged var isOwnedByMedicine: Medicine?
}
Screenshot of my data models and their relationship:
FINALLY, here is what I'm trying to do:
let predicate = NSPredicate(format: "name == %#", currentMedicine)
let fetchRequest = NSFetchRequest(entityName: "Medicine")
fetchRequest.predicate = predicate
var fetchedCurrentMedicine:Medicine!
do {
let fetchedMedicines = try managedContext.executeFetchRequest(fetchRequest) as! [Medicine]
fetchedCurrentMedicine = fetchedMedicines.first
} catch {
}
//add all the alarms to the Medicine class
for alarmString in alarmList{
let entity = NSEntityDescription.entityForName("Alarm", inManagedObjectContext: managedContext)
let alarm = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: managedContext) as! Alarm
alarm.setValue("test", forKey: "weekdays")
alarm.setValue(String(alarmString), forKey: "time")
fetchedCurrentMedicine.addAlarmObject(alarm)
}
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
It keeps failing at
fetchedCurrentMedicine.addAlarmObject(alarm)
and I get the error:
"'NSInvalidArgumentException', reason: 'NSManagedObjects of entity 'Medicine' do not support -mutableSetValueForKey: for the property 'alarms''"
Any idea where I may have messed up or gotten the schema of my data models wrong? Much appreciated.
On your data model screen alarms relation of Medicine model is to one. Perhaps medicines relation of Alarm model is to-many.
Anyway try to set to-many relation type for alarms in the data model inspector.

Failed loading designated initializer core data

I have been struggling with creating a class to manage Core Data.
I cannot seem to get passed the "Failed to call designated initializer on NSManaged Object"
How can I fix this?
This is my created class to manage the data :
import Foundation
import CoreData
import UIKit
class Hours: NSManagedObject {
#NSManaged var date: NSDate
#NSManaged var startTime: NSDate
#NSManaged var endTime: NSDate
#NSManaged var totalTime: Double
override init(entity: NSEntityDescription, insertIntoManagedObjectContext context: NSManagedObjectContext?){
let entity = NSEntityDescription.entityForName("Hours", inManagedObjectContext: context!)
super.init(entity: entity!, insertIntoManagedObjectContext: context)
}
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var context:NSManagedObjectContext {
get{
return appDel.managedObjectContext
}
}
class func insertTimes(date: NSDate, startTime:NSDate, endTime:NSDate,totalTime:Double) -> Hours{
let hour:Hours = Hours()
let newDate:Hours = NSEntityDescription.insertNewObjectForEntityForName("Hours", inManagedObjectContext: hour.context) as! Hours
print("date = \(date), start = \(startTime), end = \(endTime), total = \(totalTime)")
newDate.date = date
newDate.startTime = startTime
newDate.endTime = endTime
newDate.totalTime = totalTime
/*
newDate.setValue(date, forKey: "date")
newDate.setValue(startTime, forKey: "startTime")
newDate.setValue(endTime, forKey:"endTime")
newDate.setValue(totalTime, forKey: "totalTime")
*/
do{
try hour.context.save()
print("Succesfully saved")
}catch{
print("Unresolved save error")
}
return newDate
}
func returnDate(){
//Does not work YET
let request = NSFetchRequest(entityName: "Hours")
//request.returnsObjectsAsFaults = false
do{
let results = try context.executeFetchRequest(request)
print(results)
}catch{
print("Unresolved fetch error")
}
}
}
I try to call the method insertTimes with the following line of code, after I press a button.
#IBAction func saveData(sender: AnyObject) {
let inserted:Hours = Hours.insertTimes(date, startTime: startEndDate.startTime, endTime: startEndDate.endTime, totalTime: timeInterval)
}
Please note that all my variables are of the right types.
Source can be found on: https://github.com/bbriann123/PayDay/blob/master/Hours.swift
Look a these:
In AppDelegate you use "Loon Calculator" instead of "DayDataModel": https://github.com/bbriann123/PayDay/blob/master/Loon%20Calculator/AppDelegate.swift#L55 (the data model file name is clearly "DayDataModel": https://github.com/bbriann123/PayDay/tree/master/Loon%20Calculator/DayDataModel.xcdatamodeld/DayDataModel.xcdatamodel)
In the data model you don't specify "Hours" as the custom class of the "Hours" entity (look in the Core Data inspector)
Also, it's weird how you reach to the NSManagedObjectContext: https://github.com/bbriann123/PayDay/blob/master/Hours.swift#L25-L30
Maybe you need a better Core Data book (or whatever you're using to learn)?

Saving and retrieving custom objects in Core Data

It's the first time I'm trying to save and retrieve custom data to/from my core data, but I've run into an error saying:
fatal error: array cannot be bridged from Objective-C
when I try to load the data back.
My code looks like this, the arrayOfNames is declared as [String]:
#IBAction func saveTap(sender: AnyObject) {
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let contxt: NSManagedObjectContext = appDel.managedObjectContext!
let en = NSEntityDescription.entityForName("Indexes", inManagedObjectContext: contxt)
let arrayData: NSData = NSKeyedArchiver.archivedDataWithRootObject(arrayOfNames)
let newIndex = Indexes(entity: en!, insertIntoManagedObjectContext: contxt)
newIndex.monday = arrayData
println(newIndex.monday)
contxt.save(nil)
}
#IBAction func loadTap(sender: AnyObject) {
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext!
let fetchReq = NSFetchRequest(entityName: "Indexes")
let en = NSEntityDescription.entityForName("Indexes", inManagedObjectContext: context)
var myList:[String] = context.executeFetchRequest(fetchReq, error: nil) as! [String]
println(myList)
}
My model-file looks like this:
#objc(Indexes)
class Indexes: NSManagedObject {
#NSManaged var monday: NSData
#NSManaged var tuesday: NSData
#NSManaged var wednesday: NSData
#NSManaged var thursday: NSData
#NSManaged var friday: NSData
}
I've also set all the attributes to transformable in my data model. As I said, it's the first time I'm doing this, so sorry if the solution is obvious.
Any suggestions would be appreciated.
executeFetchRequest returns [AnyObject]?, not [String], indeed the objects in the array should be Indexes instances which contain your archived arrays of strings.
So, you need to correct the array type that the results of the fetch are being placed into.

How do I append an executeFetchRequest result into a array struct

I'm an iOS newbie developer. I'm trying to import the results of an executeFetchRequest into a structure I created for viewing later into a table. I"m getting "Array index out of range" in func getTasks(), I'm pretty sure I"m supposed to append it, but not sure quite how.
I'm sure there's a better way of setting this up in general. Right now I'm just trying to get things to work. But other suggestions would be appreciated.
import UIKit
import CoreData
var taskMgr: TaskManager = TaskManager()
struct task {
var name = "Un-Named"
var desc = "Un-Described"
}
class TaskManager: NSObject {
var tasks = task[]()
init() {
super.init()
self.getTasks()
}
func addTask(name: String, desc: String) {
tasks.append(task(name: name, desc: desc))
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext
let ent = NSEntityDescription.entityForName("Tasks", inManagedObjectContext: context)
var newTask = Tasks(entity: ent, insertIntoManagedObjectContext: context)
newTask.name = name
newTask.desc = desc
println("Object saved")
context.save(nil)
}
func getTasks() {
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext
var request = NSFetchRequest(entityName: "Tasks")
request.returnsObjectsAsFaults = false;
var results:NSArray = context.executeFetchRequest(request, error: nil)
if (results.count > 0) {
self.tasks = task[]()
var i = 0
for element in results {
tasks[i].name = element.name // fatal error: Array index out of range
tasks[i].desc = element.desc
i++
}
}
}
}
class Tasks: NSManagedObject {
#NSManaged var name: String
#NSManaged var desc: String
}
You can't use subscripting to add items to an array -- you need to call append() or use the += operator instead. Try this:
self.tasks = task[]()
for element in results {
tasks += task(name: element.name, desc: element.desc)
}

Resources