在 Swift 中使用 NSCoding 在分层 类 中进行数据持久化的最佳做法是什么?
What is best practice for data persistence in hierarchical classes using NSCoding in Swift?
问题:我已经使用 NSKeyedArchiver 在我的 iOS 应用程序中实现了数据持久化,它目前可以保存分层数据,但只能从顶层开始 class。我怎样才能从任何级别保存整个层次结构?
背景:我正在开发一个 iOS 应用程序,它使用分层 classes 的数据结构,例如学校,教室,学生。基本上,学校 class 包含一组教室(以及其他属性,如学区、名称、phone 编号等),教室 class 包含一组学生(以及其他属性)教师、房间号等属性)和学生 class 具有每个学生的属性(例如姓名、年级、课程等)。
该应用程序有三个视图控制器,一个用于层次结构的每个级别,允许在每个级别更改数据:DistrictTableViewController 有一个 School 对象数组,可以 add/delete 个数组元素,SchoolTableViewController 有一个Classroom 对象数组,并且可以 add/delete Classroom 对象数组中的元素,而 ClassroomViewController 允许用户 add/remove/edit 学生。
我已经使用 NSCoding 在所有三个 classes 中实现了数据持久性,它目前可以在层次结构中保存数据,但我只能保存来自顶级 DistrictTableVC(应用程序入口点)的数据. DistrictTableVC 有一个 saveSchools() 方法。相反,我希望能够保存来自三个 ViewController 中任何一个的更改,例如对 Student 属性 的更改将立即保存 Student 对象,以及教室中的学生数组和学校中的教室数组。
当前配置是 DistrictTableVC 将单个 School 对象传递给 SchoolTableVC,SchoolTableVC 将单个 Classroom 对象传递给 ClassroomVC。我认为我应该做的是:
- 创建一个名为 District 的新顶层 class,它包含学校数组并使用 NSCoding
- 在三个 VC 之间传递 District 对象而不是单一的较低级别对象
- 将 saveSchools() 方法从 DistrictTableVC 移至新 District class,允许我从三个 ViewController 中的任何一个调用它。
由于本人非专业,伸手看看:
- 我走在正确的轨道上吗?或
- 也许有人知道更好的方法?
感谢阅读!!
class DistrictTableViewController: UITableViewController {
private let reuseIdentifier = "schoolCell"
var schoolsArray = [School]()
override func viewDidLoad() {
super.viewDidLoad()
self.navBarTitle.title = "Schools"
// Load saved Schools if they exist, otherwise load sample data
if let savedSchools = loadSchools() {
schoolsArray += savedSchools
print("Loading saved schools")
// Update all School stats
updateSchoolListStats()
} else {
// Load the sample data
loadSampleSchools()
print("Failed to load saved data. Loading sample data...")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: TableView datasource
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return schoolsArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! SchoolTableViewCell
// Configure the cell...
let school = schoolsArray[indexPath.row]
school.calcSchoolStats()
return cell
}
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
schoolsArray.remove(at: indexPath.row)
saveSchools()
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
// Deselect any selected cells
for (_, cell) in tableView.visibleCells.enumerated() {
cell.isSelected = false
}
// SchoolTableViewCell pressed: pass the selected school to SchoolsTableViewController
if (segue.identifier ?? "") == "showSchoolDetail" {
//guard let schoolsTableViewController = segue.destination as? SchoolsTableViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
guard let selectedSchoolCell = sender as? SchoolTableViewCell else {
fatalError("Unexpected sender: \(String(describing: sender))")
}
guard let indexPath = tableView.indexPath(for: selectedSchoolCell) else {
fatalError("The selected SchoolTableViewCell is not being displayed by the table")
}
schoolTableViewController.school = schoolsArray[indexPath.row]
}
// Add button pressed: show SchoolAttributesViewController
if addBarButtonItem == sender as? UIBarButtonItem {
guard segue.destination is SchoolAttributesViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
}
}
@IBAction func unwindToSessionsTableViewController(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? SchoolsTableViewController, let school = sourceViewController.school {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing session
schoolsArray.array[selectedIndexPath.row] = school
tableView.reloadRows(at: [selectedIndexPath], with: .none)
} else {
// Add a new school to the Table View
schoolsArray.insert(session, at: 0) // Update date source; add new school to the top of the table
let newIndexPath = IndexPath(row: 0, section: 0)
tableView.insertRows(at: [newIndexPath], with: .automatic)
tableView.cellForRow(at: newIndexPath)?.isSelected = true
tableView.cellForRow(at: newIndexPath)?.selectedBackgroundView = bgColorView
}
//updateSessionListStats()
//sessionsTableView.reloadData()
saveSchools()
}
}
//MARK: Actions
private func saveSchools() {
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(schoolsArray, toFile: School.ArchiveURL.path)
if isSuccessfulSave {
os_log("Schools successfully saved", log: OSLog.default, type: .debug)
} else {
os_log("Failed to save schools...", log: OSLog.default, type: .error)
}
}
//MARK: Private Methods
private func updateSchoolListStats() {
for (_, school) in schoolsArray.array.enumerated() {
for (_, classroom) in school.classroomArray.enumerated() {
classroom.calcStats()
}
school.calcSchoolStats()
}
}
private func loadSchools() -> [School]? {
return NSKeyedUnarchiver.unarchiveObject(withFile: School.ArchiveURL.path) as? [School]
}
class School: NSObject, NSCoding {
//MARK: Properties
var name: String
var district: String
var phoneNumber: Int
var classroomArray = [Classroom]()
//MARK: Archiving Paths
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("schoolsArray")
init (name: String = "Default", district: String = "", phoneNumber: Int = -1, classroomArray = [Classroom]()) {
self.name = name
self.district = district
self.phoneNumber = phoneNumber
self.classroomArray = classroomArray
}
func calcSchoolStats() {
}
//MARK: NSCoding Protocol
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
aCoder.encode(district, forKey: "district")
aCoder.encode(phoneNumber, forKey: "phoneNumber")
aCoder.encode(classroomArray, forKey: "classroomArray")
}
required convenience init?(coder aDecoder: NSCoder) {
// The name is required. If we cannot decode a name string, the initializer should fail.
guard let name = aDecoder.decodeObject(forKey: "name") as? String else {
os_log("Unable to decode the name for a School object.", log: OSLog.default, type: .debug)
return nil
}
let district = aDecoder.decodeObject(forKey: "district") as! String
let phoneNumber = aDecoder.decodeInteger(forKey: "phoneNumber")
let classroomArray = aDecoder.decodeObject(forKey: "classroomArray") as! [Classroom]
// Must call designated initializer.
self.init(name: name, district: district, phoneNumber: phoneNumber, classroomArray: classroomArray)
}
}
class Classroom: NSObject, NSCoding {
//MARK: Properties
var teacher: String
var roomNumber: Int
var studentArray = [Student]()
//MARK: Archiving Paths
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("classroomsArray")
init (teacher: String = "", building: Int = -1, studentArray = [Student]()) {
self.teacher = teacher
self.roomNumber = roomNumber
self.studentArray = studentArray
}
func calcStats() {
}
//MARK: NSCoding Protocol
func encode(with aCoder: NSCoder) {
aCoder.encode(teacher, forKey: "teacher")
aCoder.encode(roomNumber, forKey: "roomNumber")
aCoder.encode(studentArray, forKey: "studentArray")
}
required convenience init?(coder aDecoder: NSCoder) {
// The teacher is required. If we cannot decode a teacher string, the initializer should fail.
guard let teacher = aDecoder.decodeObject(forKey: "teacher") as? String else {
os_log("Unable to decode the teacher for a Classroom object.", log: OSLog.default, type: .debug)
return nil
}
let roomNumber = aDecoder.decodeInteger(forKey: "roomNumber")
let studentArray = aDecoder.decodeObject(forKey: "studentArray") as! [Student]
// Must call designated initializer.
self.init(teacher: teacher, roomNumber: roomNumber, studentArray: studentArray)
}
}
class Student: NSObject, NSCoding {
//MARK: Properties
var first: String
var last: String
var grade: Int
var courses: [String]
//MARK: Archiving Paths
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("students")
init (first: String = "", last: String = "", grade: Int = -1, courses = [String]()) {
self.first = first
self.last = last
self.grade = grade
self.courses = courses
}
//MARK: NSCoding Protocol
func encode(with aCoder: NSCoder) {
aCoder.encode(first, forKey: "first")
aCoder.encode(last, forKey: "last")
aCoder.encode(grade, forKey: "grade")
aCoder.encode(courses, forKey: "courses")
}
required convenience init?(coder aDecoder: NSCoder) {
// The first name is required. If we cannot decode a first name string, the initializer should fail.
guard let first = aDecoder.decodeObject(forKey: "first") as? String else {
os_log("Unable to decode the first name for a Student object.", log: OSLog.default, type: .debug)
return nil
}
let last = aDecoder.decodeObject(forKey: "last") as! String
let grade = aDecoder.decodeInteger(forKey: "grade")
let courses = aDecoder.decodeObject(forKey: "courses") as! [String]
// Must call designated initializer.
self.init(first: first, last: last, grade: grade, courses: courses)
}
}
成功了!现在每个视图控制器都有一个区域对象,并且可以在数据模型修改时调用 district.saveDistrict() 。
class 地区:NSObject, NSCoding {
//MARK: Properties
var array: [School]
//MARK: Archiving Paths
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("District")
init (array: [School] = [School]()) {
self.array = array
}
//MARK: Actions
func saveDistrict() {
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(array, toFile: District.ArchiveURL.path)
if isSuccessfulSave {
os_log("Schools array successfully saved", log: OSLog.default, type: .debug)
} else {
os_log("Failed to save schools array...", log: OSLog.default, type: .error)
}
}
func loadSavedDistrict() -> District? {
var savedDistrict = District()
if let districtConst = NSKeyedUnarchiver.unarchiveObject(withFile: District.ArchiveURL.path) as? [School] {
savedDistrict = District(array: districtConst)
}
return savedDistrict
}
//MARK: NSCoding Protocol
func encode(with aCoder: NSCoder) {
aCoder.encode(array, forKey: "array")
}
required convenience init?(coder aDecoder: NSCoder) {
// The array is required. If we cannot decode the array, the initializer should fail.
guard let array = aDecoder.decodeObject(forKey: "array") as? [School] else {
os_log("Unable to decode the Schools array object.", log: OSLog.default, type: .debug)
return nil
}
// Must call designated initializer.
self.init(array: array)
}
}
问题:我已经使用 NSKeyedArchiver 在我的 iOS 应用程序中实现了数据持久化,它目前可以保存分层数据,但只能从顶层开始 class。我怎样才能从任何级别保存整个层次结构?
背景:我正在开发一个 iOS 应用程序,它使用分层 classes 的数据结构,例如学校,教室,学生。基本上,学校 class 包含一组教室(以及其他属性,如学区、名称、phone 编号等),教室 class 包含一组学生(以及其他属性)教师、房间号等属性)和学生 class 具有每个学生的属性(例如姓名、年级、课程等)。
该应用程序有三个视图控制器,一个用于层次结构的每个级别,允许在每个级别更改数据:DistrictTableViewController 有一个 School 对象数组,可以 add/delete 个数组元素,SchoolTableViewController 有一个Classroom 对象数组,并且可以 add/delete Classroom 对象数组中的元素,而 ClassroomViewController 允许用户 add/remove/edit 学生。
我已经使用 NSCoding 在所有三个 classes 中实现了数据持久性,它目前可以在层次结构中保存数据,但我只能保存来自顶级 DistrictTableVC(应用程序入口点)的数据. DistrictTableVC 有一个 saveSchools() 方法。相反,我希望能够保存来自三个 ViewController 中任何一个的更改,例如对 Student 属性 的更改将立即保存 Student 对象,以及教室中的学生数组和学校中的教室数组。
当前配置是 DistrictTableVC 将单个 School 对象传递给 SchoolTableVC,SchoolTableVC 将单个 Classroom 对象传递给 ClassroomVC。我认为我应该做的是:
- 创建一个名为 District 的新顶层 class,它包含学校数组并使用 NSCoding
- 在三个 VC 之间传递 District 对象而不是单一的较低级别对象
- 将 saveSchools() 方法从 DistrictTableVC 移至新 District class,允许我从三个 ViewController 中的任何一个调用它。
由于本人非专业,伸手看看:
- 我走在正确的轨道上吗?或
- 也许有人知道更好的方法?
感谢阅读!!
class DistrictTableViewController: UITableViewController {
private let reuseIdentifier = "schoolCell"
var schoolsArray = [School]()
override func viewDidLoad() {
super.viewDidLoad()
self.navBarTitle.title = "Schools"
// Load saved Schools if they exist, otherwise load sample data
if let savedSchools = loadSchools() {
schoolsArray += savedSchools
print("Loading saved schools")
// Update all School stats
updateSchoolListStats()
} else {
// Load the sample data
loadSampleSchools()
print("Failed to load saved data. Loading sample data...")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: TableView datasource
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return schoolsArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! SchoolTableViewCell
// Configure the cell...
let school = schoolsArray[indexPath.row]
school.calcSchoolStats()
return cell
}
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
schoolsArray.remove(at: indexPath.row)
saveSchools()
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
// Deselect any selected cells
for (_, cell) in tableView.visibleCells.enumerated() {
cell.isSelected = false
}
// SchoolTableViewCell pressed: pass the selected school to SchoolsTableViewController
if (segue.identifier ?? "") == "showSchoolDetail" {
//guard let schoolsTableViewController = segue.destination as? SchoolsTableViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
guard let selectedSchoolCell = sender as? SchoolTableViewCell else {
fatalError("Unexpected sender: \(String(describing: sender))")
}
guard let indexPath = tableView.indexPath(for: selectedSchoolCell) else {
fatalError("The selected SchoolTableViewCell is not being displayed by the table")
}
schoolTableViewController.school = schoolsArray[indexPath.row]
}
// Add button pressed: show SchoolAttributesViewController
if addBarButtonItem == sender as? UIBarButtonItem {
guard segue.destination is SchoolAttributesViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
}
}
@IBAction func unwindToSessionsTableViewController(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? SchoolsTableViewController, let school = sourceViewController.school {
if let selectedIndexPath = tableView.indexPathForSelectedRow {
// Update an existing session
schoolsArray.array[selectedIndexPath.row] = school
tableView.reloadRows(at: [selectedIndexPath], with: .none)
} else {
// Add a new school to the Table View
schoolsArray.insert(session, at: 0) // Update date source; add new school to the top of the table
let newIndexPath = IndexPath(row: 0, section: 0)
tableView.insertRows(at: [newIndexPath], with: .automatic)
tableView.cellForRow(at: newIndexPath)?.isSelected = true
tableView.cellForRow(at: newIndexPath)?.selectedBackgroundView = bgColorView
}
//updateSessionListStats()
//sessionsTableView.reloadData()
saveSchools()
}
}
//MARK: Actions
private func saveSchools() {
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(schoolsArray, toFile: School.ArchiveURL.path)
if isSuccessfulSave {
os_log("Schools successfully saved", log: OSLog.default, type: .debug)
} else {
os_log("Failed to save schools...", log: OSLog.default, type: .error)
}
}
//MARK: Private Methods
private func updateSchoolListStats() {
for (_, school) in schoolsArray.array.enumerated() {
for (_, classroom) in school.classroomArray.enumerated() {
classroom.calcStats()
}
school.calcSchoolStats()
}
}
private func loadSchools() -> [School]? {
return NSKeyedUnarchiver.unarchiveObject(withFile: School.ArchiveURL.path) as? [School]
}
class School: NSObject, NSCoding {
//MARK: Properties
var name: String
var district: String
var phoneNumber: Int
var classroomArray = [Classroom]()
//MARK: Archiving Paths
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("schoolsArray")
init (name: String = "Default", district: String = "", phoneNumber: Int = -1, classroomArray = [Classroom]()) {
self.name = name
self.district = district
self.phoneNumber = phoneNumber
self.classroomArray = classroomArray
}
func calcSchoolStats() {
}
//MARK: NSCoding Protocol
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
aCoder.encode(district, forKey: "district")
aCoder.encode(phoneNumber, forKey: "phoneNumber")
aCoder.encode(classroomArray, forKey: "classroomArray")
}
required convenience init?(coder aDecoder: NSCoder) {
// The name is required. If we cannot decode a name string, the initializer should fail.
guard let name = aDecoder.decodeObject(forKey: "name") as? String else {
os_log("Unable to decode the name for a School object.", log: OSLog.default, type: .debug)
return nil
}
let district = aDecoder.decodeObject(forKey: "district") as! String
let phoneNumber = aDecoder.decodeInteger(forKey: "phoneNumber")
let classroomArray = aDecoder.decodeObject(forKey: "classroomArray") as! [Classroom]
// Must call designated initializer.
self.init(name: name, district: district, phoneNumber: phoneNumber, classroomArray: classroomArray)
}
}
class Classroom: NSObject, NSCoding {
//MARK: Properties
var teacher: String
var roomNumber: Int
var studentArray = [Student]()
//MARK: Archiving Paths
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("classroomsArray")
init (teacher: String = "", building: Int = -1, studentArray = [Student]()) {
self.teacher = teacher
self.roomNumber = roomNumber
self.studentArray = studentArray
}
func calcStats() {
}
//MARK: NSCoding Protocol
func encode(with aCoder: NSCoder) {
aCoder.encode(teacher, forKey: "teacher")
aCoder.encode(roomNumber, forKey: "roomNumber")
aCoder.encode(studentArray, forKey: "studentArray")
}
required convenience init?(coder aDecoder: NSCoder) {
// The teacher is required. If we cannot decode a teacher string, the initializer should fail.
guard let teacher = aDecoder.decodeObject(forKey: "teacher") as? String else {
os_log("Unable to decode the teacher for a Classroom object.", log: OSLog.default, type: .debug)
return nil
}
let roomNumber = aDecoder.decodeInteger(forKey: "roomNumber")
let studentArray = aDecoder.decodeObject(forKey: "studentArray") as! [Student]
// Must call designated initializer.
self.init(teacher: teacher, roomNumber: roomNumber, studentArray: studentArray)
}
}
class Student: NSObject, NSCoding {
//MARK: Properties
var first: String
var last: String
var grade: Int
var courses: [String]
//MARK: Archiving Paths
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("students")
init (first: String = "", last: String = "", grade: Int = -1, courses = [String]()) {
self.first = first
self.last = last
self.grade = grade
self.courses = courses
}
//MARK: NSCoding Protocol
func encode(with aCoder: NSCoder) {
aCoder.encode(first, forKey: "first")
aCoder.encode(last, forKey: "last")
aCoder.encode(grade, forKey: "grade")
aCoder.encode(courses, forKey: "courses")
}
required convenience init?(coder aDecoder: NSCoder) {
// The first name is required. If we cannot decode a first name string, the initializer should fail.
guard let first = aDecoder.decodeObject(forKey: "first") as? String else {
os_log("Unable to decode the first name for a Student object.", log: OSLog.default, type: .debug)
return nil
}
let last = aDecoder.decodeObject(forKey: "last") as! String
let grade = aDecoder.decodeInteger(forKey: "grade")
let courses = aDecoder.decodeObject(forKey: "courses") as! [String]
// Must call designated initializer.
self.init(first: first, last: last, grade: grade, courses: courses)
}
}
成功了!现在每个视图控制器都有一个区域对象,并且可以在数据模型修改时调用 district.saveDistrict() 。
class 地区:NSObject, NSCoding {
//MARK: Properties
var array: [School]
//MARK: Archiving Paths
static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.appendingPathComponent("District")
init (array: [School] = [School]()) {
self.array = array
}
//MARK: Actions
func saveDistrict() {
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(array, toFile: District.ArchiveURL.path)
if isSuccessfulSave {
os_log("Schools array successfully saved", log: OSLog.default, type: .debug)
} else {
os_log("Failed to save schools array...", log: OSLog.default, type: .error)
}
}
func loadSavedDistrict() -> District? {
var savedDistrict = District()
if let districtConst = NSKeyedUnarchiver.unarchiveObject(withFile: District.ArchiveURL.path) as? [School] {
savedDistrict = District(array: districtConst)
}
return savedDistrict
}
//MARK: NSCoding Protocol
func encode(with aCoder: NSCoder) {
aCoder.encode(array, forKey: "array")
}
required convenience init?(coder aDecoder: NSCoder) {
// The array is required. If we cannot decode the array, the initializer should fail.
guard let array = aDecoder.decodeObject(forKey: "array") as? [School] else {
os_log("Unable to decode the Schools array object.", log: OSLog.default, type: .debug)
return nil
}
// Must call designated initializer.
self.init(array: array)
}
}