How to save selected pickerView value to CoreData

Hello all:


I am looking for a way to save a pickerView selection to CoreData with no success so far. I want to save the row numerical value from 0 - 6, depending on the selected value. Below is the code I am trying but it is not working. It is saving random values not the one selected:


@IBAction func saveRating(_ sender: Any) {

guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {

return

}

let managedContext = appDelegate.persistentContainer.viewContext

let entity = NSEntityDescription.entity(forEntityName: "DecisionComponents", in: managedContext)!

let component = NSManagedObject(entity: entity, insertInto: managedContext)

let whyNowTitles = ["very poor reason","poor reason","passable reason", "good reason" , "very good reason", "excellent reason"]

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {

selectedRating = whyNowTitles[row]

textLabel.text = selectedRating

let stored = row

print (stored)

print(selectedRating!)

}

for (stored) in whyNowTitles {

component.setValue(stored.self, forKeyPath: "components")

do {

try managedContext.save()

decisions.append(component)

print ("SAVED")

} catch let error as NSError {

print("Could not save. \(error), \(error.userInfo)")

}

}

}

Accepted Reply

You define row as a coinstant, not as a value you read from picker


let row = UserDefaults.standard.integer(forKey: "pickerViewRow")


If you print it in viewDidLoad, row is probably 0

print(#function, row)

And it is not changed (row ib picker(didSelectRow) is a local var


One way to correct it is to declare row as a var

(note that it would be less misleading to call it pickedRow for instance, to see the difference with row in picker delegate)


    var row = UserDefaults.standard.integer(forKey: "pickerViewRow")


So, in saveRating, row (in fact self.row) has now the correct value:


    @IBAction func saveRating(_ sender: Any){

        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {  return  }

          // pickerView components
        _ = ["insignificant", "little significance", "somewhat significant", "significant", "highly significant", "very highly significant"]

       //  Saving pickerView row
     // IN FACT, you save self.row that was updated with picker row

          let managedContext = appDelegate.persistentContainer.viewContext
          let entity = NSEntityDescription.entity(forEntityName: "DecisionComponents", in: managedContext)!
          let component = NSManagedObject(entity: entity, insertInto: managedContext)
    
            UserDefaults.standard.set(self.row, forKey: "pickerViewRow")          // row is self.row, which has been updated in picker delegate
            component.setValue(row.self, forKeyPath: "components")
            print (row) // Printing 0 instead of the selected row here
            print("User Selected \(row)") // printing  0
        do {
            try managedContext.save()
            decisions.append(component)
        
            print ("SAVED")
        
        } catch let error as NSError {
            print("Could not save. \(error), \(error.userInfo)")
        }
    }

}

In

extension RatingGLViewController: UIPickerViewDataSource, UIPickerViewDelegate {


change to update self.row


    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        selectedRating = pickerTitles[row]
        UserDefaults.standard.set(row, forKey: "pickerViewRow")
        self.row = row       // ADD THIS    // That is the pickerRow defined as a var
        textLabel.text = selectedRating
        print (selectedRating!)
        print (row)   // printing corrected of "selectedRating"
    }

Replies

You have included

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { }


in the IBAction. So it is not visible as a delegate func.


Take it out at the top level, it should work better (unless there are other errors). Take whyNowTitles out as well



let whyNowTitles = ["very poor reason","poor reason","passable reason", "good reason" , "very good reason", "excellent reason"]

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
            selectedRating = whyNowTitles[row]
            textLabel.text = selectedRating
            let stored = row
            print (stored)
            print(selectedRating!)
}

  @IBAction func saveRating(_ sender: Any) {   
            guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
                return
            }
            let managedContext = appDelegate.persistentContainer.viewContext
            let entity = NSEntityDescription.entity(forEntityName: "DecisionComponents", in: managedContext)!
            let component = NSManagedObject(entity: entity, insertInto: managedContext)

             for (stored) in whyNowTitles {
             component.setValue(stored.self, forKeyPath: "components")  
            do {
                try managedContext.save()
             decisions.append(component)
                print ("SAVED")
            } catch let error as NSError {
                print("Could not save. \(error), \(error.userInfo)")
            }

        }

    }

Hello Calude31:


My main problem is how to get the value of the selected row so I cam save it to the managedContext.

I am trying to get the value to UserDefaults then to the Managed Context but without success.


At the pickerView i see the correct number of the selected row:


func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {

selectedRating = pickerTitles[row]

UserDefaults.standard.set(row, forKey: "pickerViewRow")

textLabel.text = selectedRating

print (selectedRating!)

print (row)

}

In pickerView print (row) the value is 5 correct.



But in the below code:


guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {

return

}

let managedContext = appDelegate.persistentContainer.viewContext

let entity = NSEntityDescription.entity(forEntityName: "DecisionComponents", in: managedContext)!

let component = NSManagedObject(entity: entity, insertInto: managedContext)

UserDefaults.standard.set(row, forKey: "pickerViewRow")

component.setValue(row.self, forKeyPath: "components")

print (row)

print("User Selected \(row)")

do {

try managedContext.save()

decisions.append(component)

The print row value is 0 wrong.


How do i get the pickerView row into the managedContext????

Where is this code located ?


guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
            return
        }
    
        let managedContext = appDelegate.persistentContainer.viewContext
        let entity = NSEntityDescription.entity(forEntityName: "DecisionComponents", in: managedContext)!
        let component = NSManagedObject(entity: entity, insertInto: managedContext)
    
            UserDefaults.standard.set(row, forKey: "pickerViewRow")
            component.setValue(row.self, forKeyPath: "components")
            print (row)
            print("User Selected \(row)")
  
        do {
            try managedContext.save()
            decisions.append(component)



You set the UserDefaults with a value row.

Where is row defined in this part of code ?

Probably it is 0.


What are you trying to achieve here ?

I would understand you try to read not set the value.

To do so:

        row = defaults.integer(forKey: "pickerViewRow")          // if row is a var defined as Int somewhere

Here is the full code:


import UIKit

import CoreData


class RatingGLViewController: UIViewController, UITextViewDelegate {

@IBOutlet weak var tableView: UITableView!

@IBOutlet weak var pickerView: UIPickerView!

@IBOutlet weak var textLabel: UILabel!


let row = UserDefaults.standard.integer(forKey: "pickerViewRow")

var decisions: [NSManagedObject] = []

var selectedRating: String?


override func viewDidLoad() {

super.viewDidLoad()

self.pickerView.delegate = self

self.pickerView.dataSource = self

tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")

}

override func touchesBegan(_ touches: Set<UITouch>,

with event: UIEvent?) {

self.view.endEditing(true)

}

override func viewWillAppear (_ animated: Bool) {

super.viewWillAppear(animated)

}

override func didReceiveMemoryWarning()

{super.didReceiveMemoryWarning()

// Dispose of any resources that can be recreated.

}

@IBAction func saveRating(_ sender: Any){


guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {

return

}


// pickerView components

_ = ["insignificant", "little significance", "somewhat significant", "significant", "highly significant", "very highly significant"]


// Saving pickerView row


let managedContext = appDelegate.persistentContainer.viewContext

let entity = NSEntityDescription.entity(forEntityName: "DecisionComponents", in: managedContext)!

let component = NSManagedObject(entity: entity, insertInto: managedContext)

UserDefaults.standard.set(row, forKey: "pickerViewRow")

component.setValue(row.self, forKeyPath: "components")

print (row) // Printing 0 instead of the selected row here

print("User Selected \(row)") // printing 0

do {

try managedContext.save()

decisions.append(component)

print ("SAVED")

} catch let error as NSError {

print("Could not save. \(error), \(error.userInfo)")

}

}


}


extension RatingGLViewController: UITableViewDataSource {

func tableView(_ tableView: UITableView,

numberOfRowsInSection section: Int) -> Int {

return decisions.count

}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let component = decisions[indexPath.row]

let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

cell.textLabel?.text = component.value(forKeyPath: "component") as? String

return cell

}

func tableView(_tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {

return true

}

}


let pickerTitles = ["insignificant", "little significance", "somewhat significant", "significant", "highly significant", "very highly significant"]


extension RatingGLViewController: UIPickerViewDataSource, UIPickerViewDelegate {

func numberOfComponents(in pickerView: UIPickerView) -> Int {

return 1

}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {

// print ("TEST THREE")

return pickerTitles.count

}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?{

return pickerTitles[row]

}

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {

selectedRating = pickerTitles[row]

UserDefaults.standard.set(row, forKey: "pickerViewRow")

textLabel.text = selectedRating

print (selectedRating!)

print (row) // printing corrected of "selectedRating"

}

}

You define row as a coinstant, not as a value you read from picker


let row = UserDefaults.standard.integer(forKey: "pickerViewRow")


If you print it in viewDidLoad, row is probably 0

print(#function, row)

And it is not changed (row ib picker(didSelectRow) is a local var


One way to correct it is to declare row as a var

(note that it would be less misleading to call it pickedRow for instance, to see the difference with row in picker delegate)


    var row = UserDefaults.standard.integer(forKey: "pickerViewRow")


So, in saveRating, row (in fact self.row) has now the correct value:


    @IBAction func saveRating(_ sender: Any){

        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {  return  }

          // pickerView components
        _ = ["insignificant", "little significance", "somewhat significant", "significant", "highly significant", "very highly significant"]

       //  Saving pickerView row
     // IN FACT, you save self.row that was updated with picker row

          let managedContext = appDelegate.persistentContainer.viewContext
          let entity = NSEntityDescription.entity(forEntityName: "DecisionComponents", in: managedContext)!
          let component = NSManagedObject(entity: entity, insertInto: managedContext)
    
            UserDefaults.standard.set(self.row, forKey: "pickerViewRow")          // row is self.row, which has been updated in picker delegate
            component.setValue(row.self, forKeyPath: "components")
            print (row) // Printing 0 instead of the selected row here
            print("User Selected \(row)") // printing  0
        do {
            try managedContext.save()
            decisions.append(component)
        
            print ("SAVED")
        
        } catch let error as NSError {
            print("Could not save. \(error), \(error.userInfo)")
        }
    }

}

In

extension RatingGLViewController: UIPickerViewDataSource, UIPickerViewDelegate {


change to update self.row


    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        selectedRating = pickerTitles[row]
        UserDefaults.standard.set(row, forKey: "pickerViewRow")
        self.row = row       // ADD THIS    // That is the pickerRow defined as a var
        textLabel.text = selectedRating
        print (selectedRating!)
        print (row)   // printing corrected of "selectedRating"
    }

ANother point to explain the problem.


In picker, you saved the userDefault with the correct row value


    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        selectedRating = pickerTitles[row]
        UserDefaults.standard.set(row, forKey: "pickerViewRow")
        textLabel.text = selectedRating
        print (selectedRating!)
        print (row)   // printing corrected of "selectedRating"
    }



But in saveRating, you saved it again with the row constant value that you created when creating the class instance:

this value is 0 and it replaces the value you have just saved in picker delegate !


    @IBAction func saveRating(_ sender: Any){

        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {  return  }

// …………    
            UserDefaults.standard.set(row, forKey: "pickerViewRow")
            component.setValue(row.self, forKeyPath: "components")
            print (row) // Printing 0 instead of the selected row here
            print("User Selected \(row)") // printing  0



With the changes I propose, that will not occur anymore.

Thank you very much Claude31!


You have solved the issues i faced and I really appreciate that.