7 Replies
      Latest reply on Jan 8, 2019 5:47 PM by John368
      John368 Level 1 Level 1 (0 points)

        I'm a beginner, please help me to understand why it happens?

         

        I have two TableViewCotrollers - dynamic and static. When I add or edit item in static TVC it pass to the dynamis TVC. This data is visible in the table. But after that it is not saved. When i open the app again these changes are missing and if I roll the wheel of the pickerView these changes disappear.

         

        I put the function of saving to file on disk wherever possible, but it still does not work. What is my mistake?

         

         

        TVC 1 - Dynamic

         

        import UIKit
        
        class StudentsTableViewController: UITableViewController, UIPickerViewDataSource, UIPickerViewDelegate {
            
            @IBOutlet weak var pickerViewStudents: UIPickerView!
            var classesObject = [ClassUnit]()
            var classSelected: String = ""
            var studentsSelected: [String] = [] {
                didSet {
                    tableView.reloadData()
                }
            }
        
            override func viewDidLoad() {
                super.viewDidLoad()
                
                self.pickerViewStudents.delegate = self
                self.pickerViewStudents.dataSource = self
                
                classesObject = ClassUnit.loadFromFile()
                navigationItem.leftBarButtonItem = editButtonItem
            }
        
            func numberOfComponents(in pickerView: UIPickerView) -> Int {
                return 1
            }
        
            func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
                return classesObject.count
            }
        
            func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {        
                classesObject = ClassUnit.loadFromFile()
                return classesObject[row].classTitle
            }
            
            override func viewWillAppear(_ animated: Bool) {
                classesObject = ClassUnit.loadFromFile()
                pickerViewStudents.reloadAllComponents()
                tableView.reloadData()
            }
            
            func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
                classSelected = classesObject[row].classTitle
                studentsSelected = classesObject[row].classStudents
                ClassUnit.saveToFile(classes: classesObject)
            }
        
            // MARK: - Table view data source//
        
            override func numberOfSections(in tableView: UITableView) -> Int {
                return 1
            }
        
            override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                return studentsSelected.count
            }
            
            override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                let cell = tableView.dequeueReusableCell(withIdentifier: "ClassTitleStudents", for: indexPath)
                cell.textLabel?.text = studentsSelected[indexPath.row]
                cell.showsReorderControl = true
                ClassUnit.saveToFile(classes: classesObject)
                return cell
            }
        
            override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) ->
                UITableViewCellEditingStyle {
                    return .delete
            }
        
            override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
                if editingStyle == .delete {
                    studentsSelected.remove(at: indexPath.row)
                    tableView.deleteRows(at: [indexPath], with: .automatic)
                    ClassUnit.saveToFile(classes: classesObject)
                }
            }
        
            override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
                let moveStudent = studentsSelected.remove(at: fromIndexPath.row)
                studentsSelected.insert(moveStudent, at: to.row)
                tableView.reloadData()
                ClassUnit.saveToFile(classes: classesObject)
            }
        
            // MARK: - Navigation
        
            override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        
                if segue.identifier == "EditStudentSegue" {
                    let indexPath = tableView.indexPathForSelectedRow!
                    let student = studentsSelected[indexPath.row]
                    let nav = segue.destination as! UINavigationController
                    let  editStudentName = nav.topViewController as! AddEditStudentTableViewController
                    editStudentName.student = student
                    ClassUnit.saveToFile(classes: classesObject)
                }
            }
            
            @IBAction func unwindToStudentsTableView(segue: UIStoryboardSegue) {
                guard segue.identifier == "saveUnwindStudents" else { return }
                let sourceViewController = segue.source as! AddEditStudentTableViewController        
                let student = sourceViewController.student
                if let selectedIndexPath = tableView.indexPathForSelectedRow {
                    studentsSelected[selectedIndexPath.row] = student
                    tableView.reloadRows(at: [selectedIndexPath], with: .none)
                    ClassUnit.saveToFile(classes: classesObject)
                } else {
                    let newIndexPath = IndexPath(row: studentsSelected.count, section: 0)
                    studentsSelected.append(student)
                    //tableView.insertRows(at: [newIndexPath], with: .automatic)
                    ClassUnit.saveToFile(classes: classesObject)
                }
            }
        }

         

         

        TVC 2 - Static

         

        import UIKit
        
        class AddEditStudentTableViewController: UITableViewController {
            
            @IBOutlet weak var nameStudent: UITextField!
            
            @IBOutlet weak var saveButton: UIBarButtonItem!
            
            var classesObject = [ClassUnit]()
            
            var student = ""
        
            override func viewDidLoad() {
                super.viewDidLoad()
                
                nameStudent.text = student
                updateSveButton()
                classesObject = ClassUnit.loadFromFile()
            }
        
            func updateSveButton() {
                let nameOfStudent = nameStudent.text ?? ""
                saveButton.isEnabled = !nameOfStudent.isEmpty
                ClassUnit.saveToFile(classes: classesObject)
            }
            
            @IBAction func textEditingChanged(_ sender: UITextField) {
                updateSveButton()
            }
        
        
            // MARK: - Navigation
        
            override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
                
                guard segue.identifier == "saveUnwindStudents" else { return }
                
                let nameOfStudent = nameStudent.text ?? ""
                
                student = nameOfStudent
                
                ClassUnit.saveToFile(classes: classesObject)
            }
        }
        • Re: Why doesn't the data save in TableView?
          chasefromsparta Level 1 Level 1 (0 points)

          can I see your saveToFile() fun to see if everything is ok with that?

            • Re: Why doesn't the data save in TableView?
              John368 Level 1 Level 1 (0 points)

              Yes, of course. This method works well in two other similar TVC in this app.

               

              import Foundation
              
              class ClassUnit: Codable {
                  var classTitle: String
                  var classStudents: [String]!
                 
                  init(classTitle: String, classStudents: [String]) {
                      self.classTitle = classTitle
                      self.classStudents = classStudents
                  }
                 
                  static let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
                  static let archiveUrl = documentsDirectory.appendingPathComponent("classes").appendingPathExtension("plist")
                 
                  static func saveToFile(classes: [ClassUnit]) {
                      let propertyListEncoder = PropertyListEncoder()
                      let encodeClasses = try? propertyListEncoder.encode(classes)
                      try? encodeClasses?.write(to: archiveUrl, options: .noFileProtection)
                     
                  }
                 
                  static func loadFromFile() -> [ClassUnit] {
                      let propertyListDecoder = PropertyListDecoder()
                      var classes = [ClassUnit]()
                      if let retrivedClassesData = try? Data(contentsOf: archiveUrl), let decodeClasses = try? propertyListDecoder.decode(Array.self, from: retrivedClassesData) {
                          classes = decodeClasses
                      }
                      return classes
                  }
              }
                • Re: Why doesn't the data save in TableView?
                  chasefromsparta Level 1 Level 1 (0 points)

                  try this:

                   

                  replace saveToFile() with this:

                  static func saveToFile(classes: [ClassUnit]) {
                                      do{
                                          let data = try NSKeyedArchiver.archivedData(withRootObject: classes, requiringSecureCoding: false)
                                          UserDefaults.standard.set(data, forKey: "ClassesData")
                                      }catch {
                                          
                                      }
                                  }
                  

                  and replace loadFromFile() with this:

                  static func loadFromFile() -> [ClassUnit] {
                                      var classes = [ClassUnit]()
                                      do {
                                          let data =  UserDefaults.standard.data(forKey: "ClassesData")
                                          let decodedObject = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [ClassUnit]
                                          classes = decodedObject
                                      }
                                      return classes
                                  }
                  

                   

                  Hope it Helps 

                    • Re: Why doesn't the data save in TableView?
                      John368 Level 1 Level 1 (0 points)

                      sorry, I was sick.

                       

                      Thank you for your help. I really appreciate this.

                       

                      Today I tried to use this code, but the program gives an error. This line:

                       

                      let decodedObject = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [ClassUnit]

                       

                      This message - about (data):

                       

                      Value of optional type 'Data?' must be unwrapped to a value of type 'Data'

                       

                      I tried to use exclamation mark - (data!) - but there are new errors appear. How to fix it?

                • Re: Why doesn't the data save in TableView?
                  rsharp Level 3 Level 3 (155 points)

                  You'll definitely not want to save your file in tableView(_:cellForRowAt:).  That API is only ever going to pull values from your model object for display in the table (i.e. a read-only operation).

                   

                  You may also want to consider putting your save in just a single location.  For example, in an unwind segue if the user moves off of that screen.   If your screen is modal, you could thus save when the user taps on Done/Save to dismiss the modal UI.

                   

                  In terms of what to use for saving the file, PropertyListEncoder is definitely easier to use as it relies on the Codable protocol.  And, most times your model object's properties already honor Codbable, so no extra coding needed.   NSKeyedArchiver/Unarchiver (at least with the early versions of Swift) were extremely buggy.

                   

                  One thing about PropertyListEncoder though is that you're limited on what types you can store with that.  NSKeyedArchiver would have a leg up over that, but of course you'd have to inplement init(coder) and encode(coder) functions on your model object.

                  • Re: Why doesn't the data save in TableView?
                    John368 Level 1 Level 1 (0 points)

                    Hey, anubody can help with this question?