0

first i come from France so sorry for my english.

Second, I'm new in developpement and i have develop a code for add data and after show them with Core Data. it works.ok

but after i want update but i have a problem i don't know why i can't update my value. There is an error : "fatal error: unexpectedly found nil while unwrapping an Optional value"

i have try many solution since 1 week, but can't find the problem. Thanks if someone can help me ! even a little help :)

this is my code (swift 2.3) :

for show in table view :

import UIKit
import CoreData

class ProduitTableViewController: UITableViewController {

@IBOutlet var table: UITableView!

var produits = [NSManagedObject]()

func refreshStories(refreshControl: UIRefreshControl) {

        produits.removeAll()

        fetchData()
        self.table.reloadData()
        refreshControl.endRefreshing()

}

override func viewDidLoad() {
    super.viewDidLoad()

    self.fetchData()
    self.table.addSubview(self.refreshControl!)

    self.refreshControl?.addTarget(self, action: #selector(ProduitTableViewController.refreshStories(_:)), forControlEvents: UIControlEvents.ValueChanged)

}


override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.

}

func fetchData() {

    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate

    let managedContext = appDelegate.managedObjectContext

    //2
    let fetchRequest = NSFetchRequest(entityName: "Produits")
    let sort = NSSortDescriptor(key:"dateAjout", ascending:true)
    fetchRequest.sortDescriptors = [sort]

    //3
    do {
        let results = try managedContext.executeFetchRequest(fetchRequest)
        produits = results as! [NSManagedObject]

    } catch let error as NSError {
        print("Donnees non recu \(error), \(error.userInfo)")
    }

}

// MARK: - Table view data source

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return self.produits.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("Cell")

    let produit = produits[indexPath.row]

    cell!.textLabel!.text = produit.valueForKey("nom") as? String

    /*
    let id = produit.valueForKey("id") as? String
    let date = produit.valueForKey("date") as? NSDate
    let localNotification = UILocalNotification()
    localNotification.userInfo = ["id" : id!]
    localNotification.soundName = UILocalNotificationDefaultSoundName
    localNotification.alertBody = "expiré"
    localNotification.fireDate = date
    UIApplication.sharedApplication().scheduleLocalNotification(localNotification)
    UIApplication.sharedApplication().applicationIconBadgeNumber += 1
    */

    return cell!
}

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {

    let supprimer = UITableViewRowAction(style: .Normal, title: "Suppr.") { action, index in

        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let moc = appDelegate.managedObjectContext

        // 3
        moc.deleteObject(self.produits[indexPath.row])
        appDelegate.saveContext()

        // 4
        self.produits.removeAtIndex(indexPath.row)
        tableView.reloadData()

    }
    supprimer.backgroundColor = UIColor.redColor()

    let update = UITableViewRowAction(style: .Normal, title: "Modifier") { action, index in

    }
    update.backgroundColor = UIColor.blueColor()

    return [supprimer]
}

override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
    // the cells you would like the actions to appear needs to be editable
    return true
}

override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {

}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {

    if segue.identifier == "showDetail" {

        if let destination = segue.destinationViewController as? DetailViewController {

            let row = table.indexPathForSelectedRow?.row
            let produit = produits[row!]

            let nom = produit.valueForKey("nom") as? String
            let id = produit.valueForKey("id") as? String
            let detail = produit.valueForKey("detail") as? String
            let date = produit.valueForKey("date") as? NSDate

            let time = date
            let formatter = NSDateFormatter()
            formatter.dateFormat = "dd-MM-YY HH:mm"
            let formatteddate = formatter.stringFromDate(time!)

            destination.dataNom = nom!
            destination.dataId = id!
            destination.dataDetail = detail!
            destination.dataDate = formatteddate

        }

    }

}

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    if table.cellForRowAtIndexPath(indexPath) != nil {

        self.performSegueWithIdentifier("showDetail", sender: self)

    }

}

}

for show detail of the cell :

import CoreData
import UIKit

class DetailViewController: UIViewController {

@IBOutlet var Label: UILabel!
@IBOutlet var Detail: UITextView!
@IBOutlet weak var Date: UILabel!
@IBOutlet weak var Id: UILabel!

var dataNom = ""
var dataDetail = ""
var dataDate = ""
var dataId = ""

override func viewDidLoad() {
    super.viewDidLoad()

     Label.text = dataNom
     Detail.text = dataDetail
     Date.text = dataDate
     Id.text = dataId

    // Do any additional setup after loading the view

}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {

    if segue.identifier == "ModifierDetail" {

        if let destination = segue.destinationViewController as? ModifierViewController {

            destination.modifierNom = dataNom
            destination.modifierId = dataId 
            destination.modifierDetail = dataDetail
            destination.modifierDate = dataDate

        }

    }

}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
}
*/

}

And the last for update/modify my detail:

import UIKit
import CoreData

class ModifierViewController: UIViewController {

@IBOutlet weak var Nom: UITextField!
@IBOutlet weak var Detail: UITextView!
@IBOutlet weak var Date: UITextField!

var Produits: NSManagedObject!
var managedContext: NSManagedObjectContext!

var modifierNom = ""
var modifierDetail = ""
var modifierDate = ""
var modifierId = ""

override func viewDidLoad() {
    super.viewDidLoad()

    Nom.text = modifierNom
    Detail.text = modifierDetail
    Date.text = modifierDate

    // Do any additional setup after loading the view.

}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

@IBAction func Annuler(sender: UIBarButtonItem) {

    navigationController?.popViewControllerAnimated(true)

}


@IBAction func Modifier(sender: UIButton) {


    let fetchRequest = NSFetchRequest(entityName:"Produits")
    fetchRequest.predicate = NSPredicate(format: "nom = %@", modifierNom)

    do {

        let list = try managedContext.executeFetchRequest(fetchRequest) as! [Produit]
        if list.count == 0 // Check notificationId available then not save
        {

            let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName("Produits", inManagedObjectContext: managedContext)
            newManagedObject.setValue(modifierNom, forKey: "nom")

        }
        // success ...
    } catch let error as NSError {
        // failure
        print("Fetch failed: \(error.localizedDescription)")
    }

}
/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
}
*/

}

i have create this other file:

import Foundation
import CoreData


@objc(Produit)
class Produit: NSManagedObject {
@NSManaged var nom:String!
}
h.karam
  • 1
  • 4

1 Answers1

0

As you mentioned in the comment, this line causes the crash:

let list = try managedContext.executeFetchRequest(fetchRequest) as! [Produit]

That line is not safe, because you don't know, if casting to [Prodiut]will always be successful.

In general, you should never force-cast (as!) or force-unwrap (!) something when you don't know 1000%, that it will succeed.

To cast safely, you can use guard:

guard let list = try managedContext.executeFetchRequest(fetchRequest) as! [Produit] else {
    //do some error handling here and then return
    return
}

After that, you can safely use list.

It's really important to understand, what optionals are and how to handle them safely without crashes.

Graham
  • 7,431
  • 18
  • 59
  • 84
FelixSFD
  • 6,052
  • 10
  • 43
  • 117