Michael Craun
Michael Craun

Reputation: 91

Unable to pass Data to my second view controller

When trying to pass data that has been called from CoreData, the error returned shows Thread 1: EXC_BAD_INSTRUCTION (code=EXC_i386_INVOP, subcode=0x0). The debugger reads

fatal error: unexpectedly found nil while unwrapping an Optional value

However, when I add print statements before assigning my variables to the textfields, the variables print out correctly. I am beyond lost. Any help would be appreciated.

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
          let myVC = storyboard?.instantiateViewController(withIdentifier: "editInventoryItem") as! AddInventoryItemVC
          var inventoryItemName: String = ""
          var inventoryItemStock: Double = 0.0
          var inventoryItemPG: Double = 0.0
          var inventoryItemVG: Double = 0.0
          let itemName = flavors[indexPath.row]

          let appDelegate = UIApplication.shared.delegate as! AppDelegate
          let context = appDelegate.persistentContainer.viewContext
          let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Inventory")

          request.predicate = NSPredicate(format: "inventoryItemName ==%@", itemName)
          if let result = try? context.fetch(request) {
              for object in result as! [NSManagedObject] {
                  inventoryItemName = object.value(forKey: "inventoryItemName") as! String
                  inventoryItemStock = object.value(forKey: "inventoryItemStock") as! Double
                  inventoryItemPG = object.value(forKey: "inventoryItemPGPercent") as! Double
                  inventoryItemVG = object.value(forKey: "inventoryItemVGPercent") as! Double
              }
          } else {

          }

          myVC.itemName.text = inventoryItemName
          myVC.itemStock.text = "\(inventoryItemStock)"
          myVC.itemPG.text = "\(inventoryItemPG)"
          myVC.itemVG.text = "\(inventoryItemVG)"

          performSegue(withIdentifier: "editInventoryItem", sender: "")
      }

EDIT: After applying Rinki's suggestions, Xcode gave me the following corrections and, upon running, still returns the same errors.

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let itemName = flavors[indexPath.row]

    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    let context = appDelegate.persistentContainer.viewContext
    let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Inventory")

    request.predicate = NSPredicate(format: "inventoryItemName ==%@", itemName)
    if let result = try? context.fetch(request) {
        for object in result as! [NSManagedObject] {
            inventoryItemName = object.value(forKey: "inventoryItemName") as! String
            inventoryItemStock = object.value(forKey: "inventoryItemStock") as! Double
            inventoryItemPG = object.value(forKey: "inventoryItemPGPercent") as! Double
            inventoryItemVG = object.value(forKey: "inventoryItemVGPercent") as! Double
        }
    } else {

    }
    performSegue(withIdentifier: "editInventoryItem", sender: "")
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "editInventoryItem" {
        let myVC = segue.destination as! AddInventoryItemVC
        myVC.itemName.text = inventoryItemName
        myVC.itemStock.text = "\(inventoryItemStock)"
        myVC.itemPG.text = "\(inventoryItemPG)"
        myVC.itemVG.text = "\(inventoryItemVG)"
    }
}

UPDATE: After some printing, it now makes sense why the errors are returning. prepareForSegue() is being called before didSelectRow, so the value is currently nil, but I have no clue how to call the didSelectRow before prepareForSegue() is called.

EDIT: After doing some research and implementing a Singleton, I've fixed the issue. I've added an identifier variable in the Singleton, changing the variable to editInventoryItem when didSelectRow is called. On the second VC, I set up a simple if statement that compares identifier to editInventoryItem and fills the textfields in question.

Thank you all for all of your help. One error down, probably 5,000,000 to go! :D

Upvotes: 1

Views: 123

Answers (4)

Michael Craun
Michael Craun

Reputation: 91

On the first ViewController, my code is as follows:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let itemName = flavors[indexPath.row]

    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    let context = appDelegate.persistentContainer.viewContext
    let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Inventory")

    request.predicate = NSPredicate(format: "inventoryItemName ==%@", itemName)
    if let result = try? context.fetch(request) {
        for object in result as! [NSManagedObject] {
            Singleton.sharedInstance.itemName = object.value(forKey: "inventoryItemName") as! String
            Singleton.sharedInstance.itemStock = object.value(forKey: "inventoryItemStock") as! Double
            Singleton.sharedInstance.itemPG = object.value(forKey: "inventoryItemPGPercent") as! Double
            Singleton.sharedInstance.itemVG = object.value(forKey: "inventoryItemVGPercent") as! Double
        }
    } else {

    }
    Singleton.sharedInstance.identifier = "editInventoryItem"
}

My Singleton:

struct Singleton {
    static var sharedInstance = Singleton()

    var identifier = ""
    var itemName = ""
    var itemStock = 0.0
    var itemVG = 0.0
    var itemPG = 0.0
}

On the first ViewController, my implementing code is as follows:

override func viewDidAppear(_ animated: Bool) {
    print(Singleton.sharedInstance.identifier)
    print(Singleton.sharedInstance.itemName)

    if Singleton.sharedInstance.identifier == "editInventoryItem" {
        nicotineSwitch.isEnabled = false
        VGSwitch.isEnabled = false
        PGSwitch.isEnabled = false

        itemName.text = Singleton.sharedInstance.itemName
        itemStock.text = "\(Singleton.sharedInstance.itemStock)"
        itemPG.text = "\(Singleton.sharedInstance.itemPG)"
        itemVG.text = "\(Singleton.sharedInstance.itemVG)"
        Singleton.sharedInstance.identifier = ""
    }
}

Upvotes: 1

Srdjan
Srdjan

Reputation: 104

Maybe you can try with Singleton shared instance.

Create a new Swift file of type NSObject

YourAppState.swift

    class YourAppState : NSObject {
        class var sharedInstance: YourAppState {
          struct Static {
            static var onceToken: dispatch_once_t = 0
            static var instance: YourAppState? = nil
          }

          dispatch_once(&Static.onceToken) {
            Static.instance = YourAppState()
          }

         return Static.instance!

        }
    }

And with this you can do the following:

Create a new variable inside the shared instance file,

var inventoryItemName = ""

And before going to next VC or performing segue, give the value to inventoryItemName = object.value(forKey: "inventoryItemName") as! String. In the destination VC class pass the value like this.

self.itemName.text = YourAppState.sharedInstance().inventoryItemName

Just make sure you add the value before performing a segue.

Hope it helps, cheers!

Upvotes: 0

Ruhi
Ruhi

Reputation: 176

prepareForSegue() method will be called after calling performSegue:

performSegue(withIdentifier: "editInventoryItem", sender: nil)

Here you can send your data from SourceViewController to Destination View Controller.

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


    if(segue.identifier == "editInventoryItem") {
        let myVC = segue.destinationViewController as! NextViewController
        myVC.itemName.text = inventoryItemName
      myVC.itemStock.text = "\(inventoryItemStock)"
      myVC.itemPG.text = "\(inventoryItemPG)"
      myVC.itemVG.text = "\(inventoryItemVG)"
    }
}

Upvotes: 1

Jon Rose
Jon Rose

Reputation: 8563

Your issue has nothing to do with core data, but your use of segues. The method performSegue is create a new viewController from the storyboard. myVC that was created from instantiateViewController is discarded at the end of the function. Instead use prepareForSegue:sender: to add the values to the viewController.

Upvotes: 1

Related Questions