I am building a demo app to learn about using Core Data. But I have some issues:
Is there an easier implementation for NSManagedObjectContext
than my current one?
I don't know how to complete the code with the NSSet
The app is just a table with a list of employees's names, dates of birth and 2 tasks for each. The tasks have multiple statuses(Active, Complete,etc.). There's an item button that leads to another view where you input the name and tasks and the date of birth is set to current date.
I have 2 entities (can it be done with just EmployeeEntity
?):
EmployeeEntity
with attributes name
and dateOfBirth
+ a one-to-many tasks
relationship with destination TaskEntity
TaskEntity
with attribute name
and the inverse relationship employee
In AppDelegate(...didFinishLaunchingWithOptions...)
I set the managedObjectContext
for the 1st screen:
// Fetch Main Storyboard
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
// Instantiate Root Navigation Controller
let rootNavigationController = mainStoryboard.instantiateViewControllerWithIdentifier("StoryboardIDRootNavigationController") as! UINavigationController
// Configure View Controller
let viewController = rootNavigationController.topViewController as? TableTVC
if let viewController = viewController {
viewController.managedObjectContext = self.managedObjectContext
}
// Configure Window
window?.rootViewController = rootNavigationController
return true
Then in prepareForSegue
I pass it along (there's a managedObjectContext
variable in the destination VC):
if segue.identifier == "idTableToNew" {
if let navVC = segue.destinationViewController as? UINavigationController {
if let newVC = navVC.topViewController as? NewVC {
newVC.managedObjectContext = managedObjectContext
}
}
}
Then the fetchedResultsController
in the table VC:
lazy var frc: NSFetchedResultsController = {
// Initialize Fetch Request
let fetchRequest = NSFetchRequest(entityName: "EmployeeEntity")
// Configure Predicate
// Need to make changes because I only want today's date
let predicate = NSPredicate(format: "%K == %@", "dateOfBirth", NSDate())
fetchRequest.predicate = predicate
// Initialize Fetched Results Controller
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
// Configure Fetched Results Controller
fetchedResultsController.delegate = self
return fetchedResultsController
}()
The problem is with the data types. First of all, I don't know how to get the tasks
' statuses. Second of all, I'm not sure if this might be the best way to do it.
Below is the TasksEntity.swift
code(except import&generated comments):
enum TaskStatus: Int32 {
case Complete, Incomplete, Active, Inactive
}
class TaskEntity: NSManagedObject {
@NSManaged private var statusValue: Int32
var status: TaskStatus {
get {
return TaskStatus(rawValue: self.statusValue)!
}
set {
self.statusValue = newValue.rawValue
}
}
}
Saving the data (action for when I press an item button):
let name = nameField.text
let task1Text = task1Field.text
let task2Text = task2Field.text
// Create Entities
let employee = NSEntityDescription.entityForName("EmployeeEntity", inManagedObjectContext: managedObjectContext)
let task = NSEntityDescription.entityForName("TaskEntity", inManagedObjectContext: managedObjectContext)
// Initialize Records
let employeeRecord = EmployeeEntity(entity: employee!, insertIntoManagedObjectContext: managedObjectContext)
let taskRecord = TaskEntity(entity: task!, insertIntoManagedObjectContext: managedObjectContext)
let taskRecord2 = TaskEntity(entity: task!, insertIntoManagedObjectContext: managedObjectContext)
// Populate Records
employeeRecord.name = name
employeeRecord.dateOfBirth = NSDate()
taskRecord.taskName = task1Text
taskRecord.status = TaskStatus.Inactive // Apparently no errors so it might work
taskRecord2.taskName = task2Text
taskRecord2.status = TaskStatus.Incomplete
let taskArray = employeeRecord.mutableArrayValueForKey("tasks")
taskArray.addObject(taskRecord)
taskArray.addObject(taskRecord2)
// Save MOC
do {
try managedObjectContext.save()
} catch {
let saveError = error as NSError
print("\(saveError), \(saveError.userInfo)")
}
Now in cellForRowAtIndexPath
in the table VC (I'm stuck here):
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! EmployeeTVCell
// Fetch Record
let employeeRecord = frc.objectAtIndexPath(indexPath) as! EmployeeEntity
// Update Cell
if let name = employeeRecord.name {
cell.nameLabel.text = name
}
cell.dateLabel.text = String(employeeRecord.dateOfBirth)
if let tasksArray = employeeRecord.tasks?.mutableArrayValueForKey("tasks") {
if tasksArray.count != 0 {
cell.task1Label.text = tasksArray[0] as? String
cell.task1Label.text = tasksArray[1] as? String
print(tasksArray[0].status)
// I think that tasksArray[index] won't work but it's what I want to achieve
}
}
return cell
My guess is that I need to tell it explicitly to contain only TaskEntity
type variables in the tasks
NSSet or something like that. I saw others using Set
s. I tried Set<TaskEntity>
and I had no errors but I don't know how to adapt the code where I transform the sets into arrays.
The usual way to get the members of the Set
object as array is the method allObjects
, but apart from that you cannot cast the TaskEntity
object to String
.
...
if let tasksArray = employeeRecord.tasks?.allObjects as? [TaskEntity] where !tasksArray.isEmpty {
cell.task1Label.text = tasksArray[0].taskName
cell.task1Label.text = tasksArray[1].taskName // or is it cell.task2Label ??
print(tasksArray[0].status)
}
return cell
Consider to rename the Core Data entities and their attributes. Actually the NSManagedObject
subclasses represent always an entity so it's redundant to include Entity
in the name. Or the attribute taskName
in TaskEntity
could be just name
. While reading the code you can infer that name
belongs to Task
Regarding the status
property you need to create an Int32
attribute statusValue
in the Task
entity to store the value in Core Data. The code to map the enumeration to Int32
and vice versa is fine.