I am building an app which involves saving schedules and displaying them in a table view. I am using NSCoding to save an array of custom Schedule objects. However, when I reload the app after entering a schedule, the table view is empty again. NSKeyedArchiver and NSKeyedUnarchiver both run successfully, but I am only getting an empty array after restarting the app. What am I doing wrong?
Here is the Schedule class:
import Foundation
class Schedule: NSObject, NSCoding {
var name: String
var division: Bool
var carrierA: String
var carrierB: String
var carrierC: String
var carrierD: String
var carrierE: String
var carrierF: String
var carrierG: String
override var description: String {
return "\(name)'s schedule is: \(carrierA), \(carrierB), \(carrierC), \(carrierD), \(carrierE), \(carrierF), \(carrierG)"
}
struct PropertyKey {
static let name = "name"
static let division = "division"
static let carrierA = "carrierA"
static let carrierB = "carrierB"
static let carrierC = "carrierC"
static let carrierD = "carrierD"
static let carrierE = "carrierE"
static let carrierF = "carrierF"
static let carrierG = "carrierG"
}
static let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
static let archiveURL = documentsDirectory.appendingPathComponent("schedules")
init(name: String, division: Bool, carrierA: String, carrierB: String, carrierC: String, carrierD: String, carrierE: String, carrierF: String, carrierG: String) {
self.name = name
self.division = division
self.carrierA = carrierA
self.carrierB = carrierB
self.carrierC = carrierC
self.carrierD = carrierD
self.carrierE = carrierE
self.carrierF = carrierF
self.carrierG = carrierG
}
static func loadFromFile() -> [Schedule]? {
return NSKeyedUnarchiver.unarchiveObject(withFile: Schedule.archiveURL.path) as? [Schedule]
}
static func saveToFile(schedules: [Schedule]) {
NSKeyedArchiver.archiveRootObject(schedules, toFile: Schedule.archiveURL.path)
}
required convenience init?(coder aDecoder: NSCoder) {
guard let name = aDecoder.decodeObject(forKey: PropertyKey.name) as? String,
let division = aDecoder.decodeObject(forKey: PropertyKey.division) as? Bool,
let carrierA = aDecoder.decodeObject(forKey: PropertyKey.carrierA) as? String,
let carrierB = aDecoder.decodeObject(forKey: PropertyKey.carrierB) as? String,
let carrierC = aDecoder.decodeObject(forKey: PropertyKey.carrierC) as? String,
let carrierD = aDecoder.decodeObject(forKey: PropertyKey.carrierD) as? String,
let carrierE = aDecoder.decodeObject(forKey: PropertyKey.carrierE) as? String,
let carrierF = aDecoder.decodeObject(forKey: PropertyKey.carrierF) as? String,
let carrierG = aDecoder.decodeObject(forKey: PropertyKey.carrierG) as? String else { return nil }
self.init(name: name, division: division, carrierA: carrierA, carrierB: carrierB, carrierC: carrierC, carrierD: carrierD, carrierE: carrierE, carrierF: carrierF, carrierG: carrierG)
}
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: PropertyKey.name)
aCoder.encode(division, forKey: PropertyKey.division)
aCoder.encode(carrierA, forKey: PropertyKey.carrierA)
aCoder.encode(carrierB, forKey: PropertyKey.carrierB)
aCoder.encode(carrierC, forKey: PropertyKey.carrierC)
aCoder.encode(carrierD, forKey: PropertyKey.carrierD)
aCoder.encode(carrierE, forKey: PropertyKey.carrierE)
aCoder.encode(carrierF, forKey: PropertyKey.carrierF)
aCoder.encode(carrierG, forKey: PropertyKey.carrierG)
}
}And here is the TableViewController:
import UIKit
class SettingsTableViewController: UITableViewController {
var schedules = [Schedule]()
var previousIndex: Int?
@objc func refreshControlActivated(sender: UIRefreshControl) {
tableView.reloadData()
sender.endRefreshing()
}
/
override func viewDidLoad() {
super.viewDidLoad()
/
/
self.navigationItem.title = "Manage Schedules"
if #available(iOS 11, *) {
navigationController?.navigationBar.prefersLargeTitles = true
}
if let savedSchedules = Schedule.loadFromFile() {
print(savedSchedules)
schedules = savedSchedules
tableView.reloadData()
print("Fetched fresh, new schedules!")
print(schedules)
}
self.refreshControl = UIRefreshControl()
self.refreshControl?.addTarget(self, action: #selector(refreshControlActivated(sender:)), for: .valueChanged)
/
self.clearsSelectionOnViewWillAppear = false
/
/
}
override func viewWillAppear(_ animated: Bool) {
tableView.reloadData()
/
}
override func viewWillDisappear(_ animated: Bool) {
Schedule.saveToFile(schedules: schedules)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
/
}
/
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return schedules.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
/
let schedule = schedules[indexPath.row]
cell.textLabel?.text = schedule.name
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
previousIndex = indexPath.row
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
/
/
guard let formViewController = segue.destination as? FormViewController else { return }
if let indexPath = tableView.indexPathForSelectedRow,
segue.identifier == "editSchedule" {
formViewController.schedule = self.schedules[indexPath.row]
}
}
@IBAction func unwindToTable(segue: UIStoryboardSegue) {
guard let source = segue.source as? FormViewController, let schedule = source.schedule else { return }
if let indexPath = tableView.indexPathForSelectedRow {
schedules.remove(at: indexPath.row)
schedules.insert(schedule, at: indexPath.row)
schedules.remove(at: indexPath.row)
tableView.deselectRow(at: indexPath, animated: true)
tableView.reloadData()
} else {
schedules.append(schedule)
}
Schedule.saveToFile(schedules: schedules)
}
}