This is my code:
import UIKit
import CoreData
class ExerciseViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
override func viewDidLoad() {
super.viewDidLoad()
//sets stepper configs
setsStepper.wraps = false
setsStepper.autorepeat = true
setsStepper.continuous = true
setsStepper.tintColor = UIColor.redColor()
setsStepper.minimumValue = 0
setsStepper.maximumValue = 500
setsStepper.value = 0
//reps stepper configs
repsStepper.wraps = false
repsStepper.autorepeat = true
repsStepper.continuous = true
repsStepper.tintColor = UIColor.orangeColor()
repsStepper.minimumValue = 0
repsStepper.maximumValue = 500
repsStepper.value = 0
exerciseTableView.reloadData()
}
var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var fetchedResultsController: NSFetchedResultsController?
@IBOutlet var exerciseTableView: UITableView!
@IBOutlet var dayName: UITextField!
@IBOutlet var exerciseName: UITextField!
@IBOutlet var setsStepper: UIStepper!
@IBOutlet var repsStepper: UIStepper!
@IBOutlet var setsNumber: UILabel!
@IBOutlet var repsNumber: UILabel!
var daysArray = [String]()
var namesArray = [String]()
var setsArray = [Int]()
var repsArray = [Int]()
func appendDaysToArray() {
let dayLabel = dayName.text
daysArray.append(dayLabel)
let entityDescription = NSEntityDescription.entityForName("TrainingDay", inManagedObjectContext: moc!)
let trainingday = TrainingDay(entity: entityDescription!, insertIntoManagedObjectContext: moc)
trainingday.day = dayName.text
var error: NSError?
moc?.save(&error)
if let err = error {
var status = err.localizedFailureReason
println("\(status)")
} else {
println("Day #\(dayName.text) saved successfully!")
}
}
func appendNamesToArray () {
let nameLabel = exerciseName.text
namesArray.append(nameLabel)
let entityDescription = NSEntityDescription.entityForName("TrainingDetails", inManagedObjectContext: moc!)
let trainingdetails = TrainingDetails(entity: entityDescription!, insertIntoManagedObjectContext: moc)
trainingdetails.exerciseName = exerciseName.text
var error: NSError?
moc?.save(&error)
if let err = error {
var status = err.localizedFailureReason
println("\(status)")
} else {
println("Exercise: #\(exerciseName.text) saved successfully!")
}
}
func appendNumberToSets () {
let numberOfSets = setsNumber.text?.toInt()
setsArray.append(numberOfSets!)
let entityDescription = NSEntityDescription.entityForName("TrainingDetails", inManagedObjectContext: moc!)
let trainingdetails = TrainingDetails(entity: entityDescription!, insertIntoManagedObjectContext: moc)
trainingdetails.setsNumber = setsNumber.text!
var error: NSError?
moc?.save(&error)
if let err = error {
var status = err.localizedFailureReason
println("\(status)")
} else {
println("Exercise: #\(setsNumber.text) saved successfully!")
}
}
func appendNumberOfReps () {
let numberOfReps = repsNumber.text?.toInt()
repsArray.append(numberOfReps!)
let entityDescription = NSEntityDescription.entityForName("TrainingDetails", inManagedObjectContext: moc!)
let trainingdetails = TrainingDetails(entity: entityDescription!, insertIntoManagedObjectContext: moc)
trainingdetails.repsNumber = repsNumber.text!
var error: NSError?
moc?.save(&error)
if let err = error {
var status = err.localizedFailureReason
println("\(status)")
} else {
println("Exercise: #\(repsNumber.text) saved successfully!")
}
}
@IBAction func doneButton(sender: AnyObject) {
println("\(dayName.text)")
appendDaysToArray()
println("\(exerciseName.text)")
appendNamesToArray()
println("\(setsNumber.text)")
appendNumberToSets()
println("\(repsNumber.text)")
appendNumberOfReps()
exerciseTableView.reloadData()
}
@IBAction func setsStepperAction(sender: UIStepper) {
println("\(Int(sender.value))")
setsNumber.text = Int(sender.value).description
}
@IBAction func repsStepper(sender: UIStepper) {
println("\(Int(sender.value))")
repsNumber.text = Int(sender.value).description
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return namesArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "exerciseCell"
var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell
if cell == nil {
cell = UITableViewCell(style: UITableViewCellStyle.Value2, reuseIdentifier: cellIdentifier)
}
let row = indexPath.row
let name = namesArray[indexPath.row]
let numberReps = repsArray[indexPath.row]
let numberSets = setsArray[indexPath.row]
cell!.textLabel!.text = name
cell?.detailTextLabel?.text = "Sets: #\(numberSets) Reps: #\(numberReps)"
return cell!
}
}
and
class ViewExercisesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, NSFetchedResultsControllerDelegate {
override func viewDidLoad() {
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchTrainingDetails(), managedObjectContext: moc!, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController?.delegate = self
fetchedResultsController?.performFetch(nil)
self.viewExerciseTableView.reloadData()
sundayButton.frame = CGRectMake(-30,150,125,125)
sundayButton.addTarget(self, action: "sundayButtonTouch:", forControlEvents: UIControlEvents.TouchDown)
sundayButton.setImage(imageSunday, forState: .Normal)
sundayButton.imageEdgeInsets = UIEdgeInsetsMake(30,30,30,30)
self.view.addSubview(sundayButton)
mondayButton.frame = CGRectMake(120,150,125,125)
mondayButton.addTarget(self, action: "mondayButtonTouch:", forControlEvents: UIControlEvents.TouchDown)
mondayButton.setImage(imageMonday, forState: .Normal)
mondayButton.imageEdgeInsets = UIEdgeInsetsMake(30,30,30,30)
self.view.addSubview(mondayButton)
tuesdayButton.frame = CGRectMake(270,150,125,125)
tuesdayButton.addTarget(self, action: "tuesdayButtonTouch:", forControlEvents: UIControlEvents.TouchDown)
tuesdayButton.setImage(imageTuesday, forState: .Normal)
tuesdayButton.imageEdgeInsets = UIEdgeInsetsMake(30,30,30,30)
self.view.addSubview(tuesdayButton)
wednesdayButton.frame = CGRectMake(-30,250,125,125)
wednesdayButton.addTarget(self, action: "wednesdayButtonTouch:", forControlEvents: UIControlEvents.TouchDown)
wednesdayButton.setImage(imageWednesday, forState: .Normal)
wednesdayButton.imageEdgeInsets = UIEdgeInsetsMake(30,30,30,30)
self.view.addSubview(wednesdayButton)
thursdayButton.frame = CGRectMake(70,250,125,125)
thursdayButton.addTarget(self, action: "thursdayButtonTouch:", forControlEvents: UIControlEvents.TouchDown)
thursdayButton.setImage(imageThursday, forState: .Normal)
thursdayButton.imageEdgeInsets = UIEdgeInsetsMake(30,30,30,30)
self.view.addSubview(thursdayButton)
fridayButton.frame = CGRectMake(170,250,125,125)
fridayButton.addTarget(self, action: "fridayButtonTouch:", forControlEvents: UIControlEvents.TouchDown)
fridayButton.setImage(imageFriday, forState: .Normal)
fridayButton.imageEdgeInsets = UIEdgeInsetsMake(30,30,30,30)
self.view.addSubview(fridayButton)
saturdayButton.frame = CGRectMake(270,250,125,125)
saturdayButton.addTarget(self, action: "saturdayButtonTouch:", forControlEvents: UIControlEvents.TouchDown)
saturdayButton.setImage(imageSaturday, forState: .Normal)
saturdayButton.imageEdgeInsets = UIEdgeInsetsMake(30,30,30,30)
self.view.addSubview(saturdayButton)
}
//VAR AND LET
var sundayButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
var imageSunday = UIImage(named: "day.png")
var mondayButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
var imageMonday = UIImage(named: "day.png")
var tuesdayButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
var imageTuesday = UIImage(named: "day.png")
var wednesdayButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
var imageWednesday = UIImage(named: "day.png")
var thursdayButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
var imageThursday = UIImage(named: "day.png")
var fridayButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
var imageFriday = UIImage(named: "day.png")
var saturdayButton = UIButton.buttonWithType(UIButtonType.Custom) as! UIButton
var imageSaturday = UIImage(named: "day.png")
@IBOutlet var viewExerciseTableView: UITableView!
var moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
var fetchedResultsController: NSFetchedResultsController?
// FUNCTIONS
func sundayButtonTouch(sender: UIButton!) {
println("future event will be added, button working fine - sunday")
}
func mondayButtonTouch(sender: UIButton!) {
println("future event will be added, button working fine - monday")
}
func tuesdayButtonTouch(sender: UIButton!) {
println("future event will be added, button working fine - tuesday")
}
func wednesdayButtonTouch(sender: UIButton!) {
println("future event will be added, button working fine - wednesday")
}
func thursdayButtonTouch(sender: UIButton!) {
println("future event will be added, button working fine - thursday")
}
func fridayButtonTouch(sender: UIButton!) {
println("future event will be added, button working fine - friday")
}
func saturdayButtonTouch(sender: UIButton!) {
println("future event will be added, button working fine - saturday")
}
// FETCH REQUEST METHODS
func fetchTrainingDay() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "TrainingDay")
// let predicate = NSPredicate(format: "day == %@")
let sortDescriptor = NSSortDescriptor(key: "day", ascending: true)
fetchRequest.predicate = nil
fetchRequest.sortDescriptors = [sortDescriptor]
fetchRequest.fetchBatchSize = 20
return fetchRequest
}
func fetchTrainingDetails() -> NSFetchRequest {
let fetchRequest = NSFetchRequest(entityName: "TrainingDetails")
fetchRequest.predicate = nil
let sortDescriptor1 = NSSortDescriptor(key: "exerciseName", ascending: true)
let sortDescriptor2 = NSSortDescriptor(key: "repsNumber", ascending: true)
let sortDescriptor3 = NSSortDescriptor(key: "setsNumber", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor1, sortDescriptor2, sortDescriptor3]
fetchRequest.fetchBatchSize = 20
return fetchRequest
}
//TABLE VIEW DELEGATE METHODS
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fetchedResultsController?.sections?[section].numberOfObjects ?? 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "exCell"
var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell
if let exCell = fetchedResultsController?.objectAtIndexPath(indexPath) as? TrainingDetails {
println("THE ERROR IS HERE #\(exCell.exerciseName) \(exCell.repsNumber) \(exCell.setsNumber)")
cell!.textLabel?.text = exCell.exerciseName
cell?.detailTextLabel?.text = "Sets: #\(exCell.setsNumber) Reps: #\(exCell.repsNumber)"
}
return cell!
}
// MARK: NSFetchedResultsControllerDelegate
func controllerWillChangeContent(controller: NSFetchedResultsController) {
self.viewExerciseTableView.beginUpdates()
}
func controller(controller: NSFetchedResultsController,
didChangeObject anObject: AnyObject,
atIndexPath indexPath: NSIndexPath?,
forChangeType type: NSFetchedResultsChangeType,
newIndexPath: NSIndexPath?)
{
switch(type) {
case .Insert:
if let newIndexPath = newIndexPath {
viewExerciseTableView.insertRowsAtIndexPaths([newIndexPath],
withRowAnimation:UITableViewRowAnimation.Fade)
}
case .Delete:
if let indexPath = indexPath {
viewExerciseTableView.deleteRowsAtIndexPaths([indexPath],
withRowAnimation: UITableViewRowAnimation.Fade)
}
case .Update:
break
case .Move:
if let indexPath = indexPath {
if let newIndexPath = newIndexPath {
viewExerciseTableView.deleteRowsAtIndexPaths([indexPath],
withRowAnimation: UITableViewRowAnimation.Fade)
viewExerciseTableView.insertRowsAtIndexPaths([newIndexPath],
withRowAnimation: UITableViewRowAnimation.Fade)
}
}
}
}
func controller(controller: NSFetchedResultsController,
didChangeSection sectionInfo: NSFetchedResultsSectionInfo,
atIndex sectionIndex: Int,
forChangeType type: NSFetchedResultsChangeType)
{
switch(type) {
case .Insert:
viewExerciseTableView.insertSections(NSIndexSet(index: sectionIndex),
withRowAnimation: UITableViewRowAnimation.Fade)
case .Delete:
viewExerciseTableView.deleteSections(NSIndexSet(index: sectionIndex),
withRowAnimation: UITableViewRowAnimation.Fade)
default:
break
}
}
func controllerDidChangeContent(controller: NSFetchedResultsController) {
viewExerciseTableView.endUpdates()
}
}
and
import Foundation
import CoreData
class TrainingDetails: NSManagedObject {
@NSManaged var exerciseName: String
@NSManaged var setsNumber: String
@NSManaged var repsNumber: String
@NSManaged var relationship2: NSManagedObject
}
The logic is this: In "ExerciseViewController", I put some stuff in CoreData, this view works fine. Now, in ViewExercisesViewController, I should retrieve the exerciseName and set it as the cell's textLabel, but it crashes right here: println("THE ERROR IS HERE #\(exCell.exerciseName) \(exCell.repsNumber) \(exCell.setsNumber)")
. It seems that I'm not retrieving it correctly, but I'm certain it is being saved in CoreData.
Anyone has any idea? I have an app that does something similar, but this one is not working.
Thanks for your help, I'm looking forward to figure out what's wrong here.
EDIT -> image with crush error:
EDIT 2
leads to this:
FINAL EDIT -> SOLUTION
As Tom said, doing this:
func appendTrainingDetails () {
let nameLabel = exerciseName.text
namesArray.append(nameLabel)
let numberOfSets = setsNumber.text?.toInt()
setsArray.append(numberOfSets!)
let numberOfReps = repsNumber.text?.toInt()
repsArray.append(numberOfReps!)
let entityDescription = NSEntityDescription.entityForName("TrainingDetails", inManagedObjectContext: moc!)
let trainingdetails = TrainingDetails(entity: entityDescription!, insertIntoManagedObjectContext: moc)
trainingdetails.exerciseName = exerciseName.text
trainingdetails.setsNumber = setsNumber.text!
trainingdetails.repsNumber = repsNumber.text!
var error: NSError?
moc?.save(&error)
if let err = error {
var status = err.localizedFailureReason
println("\(status)")
} else {
println("Exercise: #\(exerciseName.text) saved successfully!")
println("Number of sets: #\(setsNumber.text) saved successfully!")
println("Number of reps: #\(repsNumber.text) saved successfully!")
}
}
with the methods, led me to fix what I was doing wrong! Thank you very very much!!
Here's what it looks like is happening...
Your methods appendNumberToSets()
, appendNumberOfReps()
, and appendNamesToArray()
all have this code:
let entityDescription = NSEntityDescription.entityForName("TrainingDetails", inManagedObjectContext: moc!)
let trainingdetails = TrainingDetails(entity: entityDescription!, insertIntoManagedObjectContext: moc)
That means that each one of these is creating a new instance of TrainingDetails
. However,
appendNumberToSets()
only sets a value for the setsNumber
propertyappendNamesToArray()
only sets a value for the exerciseName
propertyappendNumberOfReps()
only sets a value for the repsNumber
propertyIn all three methods you're creating new TrainingDetails
instances but leaving most of the properties with nil values. That would be OK except that later on you attempt to look up exerciseName
, setsNumber
, and repsNumber
on the same instance. Since none of your instances have values for all three, your app crashes.
This is happening because Core Data has an idea of what an optional value is, and Swift also has an idea of what "optional" means, but these are not the same thing. Core Data doesn't care that you're leaving these properties with nil values, but Swift does. The conflict between these two ideas is leading to your problem.
What you should do:
TrainingDetails
class to make them Swift optionals. It looks like you're using code that Xcode generated, but that code is wrong and it's completely OK to fix it. The compiler will then force you to check whether the attributes have values, and you won't crash.TrainingDetails
must have values for every one of these attributes, you should do two things. First, edit your Core Data model. For each of these attributes, un-check the "Optional" box in the model editor. That way Core Data will know that nil isn't allowed and it will prevent you from saving changes if you have unexpected nils. Second, you need to change the three methods mentioned above to either (a) always assign values to all of the attributes, or (b) re-use the same TrainingDetails
instance instead of creating a new one in each method (the choice depends on your requirements, so it's up to you to decide which is correct).