I have been searching for days on how to work with one to many relationships. But I can't get it figured out.
I am working on a app which stores the grades of a subject. So one subject has a to-many relationship with grades. All the data is currently stored in a array.
viewModel
Where the the [Double]'s - Cijfer and weging- are attributes of a grade.
I currently have the following for saving the data:
var vakken: [(naam:String, voorkeurGemiddelde:Double, wegingGemiddelde:Double, cijfer:[Double], weging:[Double], gemiddelde:Double)] = []
I am using the data in a tableView.
I have seen in some tutorials that the use: let context = AppDelegate().managedObjectContext
// Create subject
let entitySubject = NSEntityDescription.entityForName("Subject", inManagedObjectContext: context)
let Subject = NSManagedObject(entity: entitySubject!, insertIntoManagedObjectContext: context)
// Populate subject
Subject.setValue(vakken.last!.naam, forKey: "name")
Subject.setValue(vakken.last!.voorkeurGemiddelde, forKey: "voorkeurGemiddelde")
Subject.setValue(vakken.last!.wegingGemiddelde, forKey: "wegingNP")
Subject.setValue(vakken.last!.gemiddelde, forKey: "gemiddelde")
for var j = 0; j < vakken.last!.cijfer.count-1; j++ {
let Grade = NSEntityDescription.insertNewObjectForEntityForName("Grade", inManagedObjectContext: context)
Grade.setValue(vakken.last!.cijfer[j], forKey: "cijfer")
Grade.setValue(vakken.last!.weging[j], forKey: "weging")
Subject.setValue(NSSet(object: Grade), forKey: "grade")
}
do{
try context.save()
}catch{
print("error")
}
and then use it in a tableview.
1. But is this also possible with the model I have, and should I use it?
I first thought was to just save the data when the app terminates, so I would have the code in the app delegate, and then just retrive it when the app starts.
2. But how would I get the array to the appdelegate and would this be smart?
And for fetching the data I have this:
var vakken = [NSManagedObject]()
3. But how would I get from the result my array back or if I use NSManagedObject how can I refer to the attributes and in most particular the grades.
I also sort my array by name which can be A-Z or Z-A, and I sort by average which can be 10-1 or 1-10. I have the code to sort it in my array.
4. But I don't know how to sort this with core data.
I have also seen a tutorial where they made a class for the attributes link. So I made one my self:
let fetchRequest = NSFetchRequest(entityName: "Subject")
// // Add Sort Descriptor
// let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
// fetchRequest.sortDescriptors = [sortDescriptor]
let context = Appdelegate().managedObjectContext
// Execute Fetch Request
do {
let result = try context.executeFetchRequest(fetchRequest)
print(result)
} catch {
let fetchError = error as NSError
print(fetchError)
}
5. But I don't know if I need it nor how to use it?
If you have any advise, tips, know good tutorials or know one of these questions. Your help would be appreciated.
If you need any more information just let me know. :)
Update:
I have this code to get the object of the subject witch is clicked.
import UIKit
import CoreData
import Foundation
class Subject: NSManagedObject {
@NSManaged var gemiddelde: Double
@NSManaged var name: String
@NSManaged var voorkeurGemiddelde: Double
@NSManaged var wegingNP: Double
@NSManaged var Grades: NSSet
}
class Grade: NSManagedObject {
@NSManaged var cijfer: Double
@NSManaged var weging: Double
@NSManaged var subject: Subject
}
extension Subject {
func addTagObject(value:Grade) {
let items = self.mutableSetValueForKey("grade");
items.addObject(value)
}
func removeTagObject(value:Grade) {
let items = self.mutableSetValueForKey("grade");
items.removeObject(value)
}
}
And this is the code on the receiving end:
if let indexPath = self.tableView.indexPathForSelectedRow {
let object = self.fetchedResultsController.objectAtIndexPath(indexPath)
let controller = segue.destinationViewController as! VakTableViewController
controller.vak = object as! Subject
}
6. But how can i delete on of the grades?
I have tried this:
var vak: Subject! {
didSet {
// Update the view.
cijfers = vak.valueForKeyPath("grades.cijfer")!.allObjects as! [Double]
wegingen = vak.valueForKeyPath("grades.weging")!.allObjects as! [Double]
self.tableView.reloadData()
}
}
var cijfers: Array<Double>!
var wegingen: Array<Double>!
But this doesn't work.
7. And how can i save the vak(subject) to the context when i just change own of the variable?
like this: vak.gemiddelde = newgemiddelde
答案 0 :(得分:6)
直接解决您的问题:
我正在使用tableView中的数据。我在一些教程中看到使用:
var vakken = [NSManagedObject]()
然后在tableview中使用它。 这也适用于我拥有的模型,我应该使用它吗
这当然是可能的,但使用NSFetchedResultsController
可能会更好(参见Apple Docs)。这是专门设计用于使用Core-Data中的数据轻松填充tableView。
我首先想到的是在应用程序终止时只保存数据,所以我会在应用程序委托中获得代码,然后在应用程序启动时进行检索。 如何将数组添加到appdelegate并且这样聪明?
我不会在App Delegate中填充/保存数组。有些人关注Apple的模板项目,这些项目在App Delegate中构建CoreData堆栈;其他人有一个单独的类,他们实例化来管理堆栈。从代码的外观来看,它目前使用前者。然后,您的视图控制器可以使用以下命令从App Delegate获取NSManagedObjectContext
:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context = appDelegate.managedObjectContext
(正如beyowulf指出的那样,你不应该使用AppDelegate().managedObjectContext
,因为它会创建一个新实例,而不是引用现有实例)。一旦他们有了上下文,您的视图控制器就可以获取他们需要的数据,添加或更新现有记录等等。
但是如何从结果中获取我的数组,或者如果我使用NSManagedObject,我如何引用属性,尤其是等级。
result
是NSManagedObjects
的数组。您可以使用valueForKey
获取属性值,&#34;读取&#34;相当于setValue:(_, forKey:)
:
let firstObject = result[0]
let firstObjectName = firstObject.valueForKey("name")
以同样的方式,可以通过以下方式获得成绩:
let firstObjectGrades = firstObject.valueForKey("grades")
但有更好的方法:请参阅下面的最后一个问题。
我也按名称对数组进行排序,可以是A-Z或Z-A,我按平均值排序,可以是10-1或1-10。我有代码在我的数组中对它进行排序。 但我不知道如何使用核心数据对此进行排序。
最简单的方法是在获取数据时对数据进行排序。为此,请为fetch指定NSSortDescriptor
(请参阅Apple Docs):
let fetchRequest = NSFetchRequest(entityName: "Subject")
fetchRequest.sortDescriptors = [NSSortDescriptor(key:"name", ascending:true)
我还看过一个教程,他们为这些属性创建了一个类。 我不知道我是否需要它以及如何使用它?
是的,请使用它。它会让生活变得更加轻松。而不是编写自己的类定义,使用Xcode菜单选项&#34;创建NSManagedObject子类&#34;在数据模型编辑器中。它将为您创建类代码,并且还将每个实体设置为使用相应的类。 (如果您希望坚持使用自己的代码,则需要修改数据模型编辑器中每个实体的&#34;类&#34;。
一旦正确定义了子类,就可以使用点表示法引用属性和关系,而不是需要使用valueForKey
和setValueForKey
:
所以:
let Subject = NSManagedObject(entity: entitySubject!, insertIntoManagedObjectContext: context)
将成为:
let subject = Subject(entity: entitySubject!, insertIntoManagedObjectContext: context)
(注意,变量应以小写字母开头 - 类和实体名称以大写字母开头)。
Grade.setValue(vakken.last!.cijfer[j], forKey: "cijfer")
变为:
grade.cijfer = vakken.last!.cijfer[j]
和
let firstObjectGrades = firstObject.valueForKey("grades")
变为:
let firstObjectGrades = firstObject.grades
此外,addTagObject
和removeTagObject
功能可以更轻松地管理多对多关系。您的代码目前有:
Subject.setValue(NSSet(object: Grade), forKey: "grade")
这将替换<{em> Subject
Grade
对象的所有现有成绩:它不会将其添加到现有成绩中。事实上,对于一对多的关系来说,管理逆向(一对一)关系要容易得多:
grade.subject = subject
CoreData将自动为您处理逆(to-many)关系。
如何删除其中一个成绩?
首先,不要为每个Grades
属性构建单独的数组:只有Grades
的一个数组:
var vak: Subject! {
didSet {
// Update the view.
grades = vak.grades.allObjects as! [Grade]
self.tableView.reloadData()
}
}
var grades: Array<Grade>!
您可以在需要时轻松获取属性。例如,在您的cellForRowAtIndexPath
中,您可能会遇到以下情况:
let grade = grades[indexPath.row]
let cijfer = grade.cijfer
let weging = grade.weging
// populate cell labels etc using "cijfer" and "waging"
您的commitEditingStyle
可以轻松找到正在删除的Grade
,并据此处理:
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
let grade = grades[indexPath.row]
// either remove the Grade from your Subject (which leaves
// the Grade "orphaned"):
grade.subject = nil
// or (probably better) completely delete the grade:
managedobjectcontext.deleteObject(grade)
// then remove it from the "grades" array:
grades.removeAtIndex(indexPath.row)
// and finally delete the corresponding table view row
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
// wrap up by saving the changes to the context:
do{
try mangedobjectcontext.save()
}catch{
print("error")
}
}
}
当我只更改其中一个变量时,如何将vak(主题)保存到上下文中?
每当您保存上下文时,它会将所有更改保存到使用它注册的任何对象(插入其中或使用它获取)。因此,即使您所做的唯一更改是修改一个Subject
的一个属性,也只需在上下文中调用save()
。