Search code examples
arraysswiftdata-persistence

Empty array when loading data in Swift 3


I'm new to swift and was trying to teach myself data persistence using Apple's "App Development with Swift" iBook. I'm trying to create and store an array of students that I can then recall later on. The issue is, when I try to reload them, the array is empty. I really can't understand what's wrong since I get no errors, just an empty array. This is what I see when I run the code. Can someone please show me what I'm doing wrong? Any help is appreciated but it may need to be dumbed down for me. Cheers.

import Foundation

class Student: NSObject, NSCoding {

    var idNumber: String
    var forename: String
    var surname: String
    var homeAddress: String
    var postcode: String
    var homePhone: String
    var gender: String
    var tutorGroup: String
    var schoolEmail: String
    var dateOfBirth: String



    // property key strings are used for en and de coding later
    struct PropertyKey {
        static let idNumber = "idNumber"
        static let forename = "forename"
        static let surname = "surname"
        static let homeAddress = "homeAddress"
        static let postcode = "postcode"
        static let homePhone = "homePhone"
        static let gender = "gender"
        static let tutorGroup = "tutorGroup"
        static let schoolEmail = "schoolEmail"
        static let dateOfBirth = "dateOfBirth"
    }

        static let DocumentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    static let ArchiveURL = DocumentsDirectory.appendingPathComponent("students")



    init(idNumber: String, forename: String, surname: String,
         homeAddress: String, postcode: String, homePhone: String,
         gender: String, tutorGroup: String, schoolEmail: String,
         dateOfBirth: String) {
        self.idNumber = idNumber
        self.forename = forename
        self.surname = surname
        self.homeAddress = homeAddress
        self.postcode = postcode
        self.homePhone = homePhone
        self.gender = gender
        self.tutorGroup = tutorGroup
        self.schoolEmail = schoolEmail
        self.dateOfBirth = dateOfBirth
    }


    static func loadFromFile() -> [Student]  {
        return NSKeyedUnarchiver.unarchiveObject(withFile: Student.ArchiveURL.path) as! [Student]
    }


    static func loadSampleStudents() -> [Student] {
        return [
            Student(idNumber: "13LDL", forename: "Dean", surname: "Lobo", homeAddress: "Neverland", postcode: "Far away", homePhone: "11111111", gender: "male", tutorGroup: "11L", schoolEmail: "[email protected]", dateOfBirth: "02/03/04"),

            Student(idNumber: "14LSD", forename: "Harry", surname: "Harrison", homeAddress: "21 Bushey Mill Lane", postcode: "WD23 2DD", homePhone: "01923218324", gender: "male", tutorGroup: "11L", schoolEmail: "[email protected]", dateOfBirth: "02/03/05"),

            Student(idNumber: "11LSA", forename: "Stefan", surname: "Stephens", homeAddress: "Somewhere near school", postcode: "SA12 2WP", homePhone: "01923123123", gender: "male", tutorGroup: "11L", schoolEmail: "[email protected]", dateOfBirth: "08/03/01"),

            Student(idNumber: "11LPJ", forename: "Penny", surname: "Pennyworth", homeAddress: "12 Downing Street", postcode: "W1 12AP", homePhone: "0192311111", gender: "female", tutorGroup: "11L", schoolEmail: "[email protected]", dateOfBirth: "08/03/02"),


        ]
    }

    static func saveToFile(students: [Student]) {
        NSKeyedArchiver.archiveRootObject(students, toFile: Student.ArchiveURL.path)
    }




    required convenience init?(coder aDecoder: NSCoder) {
        guard let idNumber = aDecoder.decodeObject(forKey: PropertyKey.idNumber) as? String,
            let forename = aDecoder.decodeObject(forKey: PropertyKey.forename) as? String,
            let surname = aDecoder.decodeObject(forKey: PropertyKey.surname) as? String,
            let homeAddress = aDecoder.decodeObject(forKey: PropertyKey.homeAddress) as? String,
            let postcode = aDecoder.decodeObject(forKey: PropertyKey.postcode) as? String,
            let homePhone = aDecoder.decodeObject(forKey: PropertyKey.homePhone) as? String,
            let gender = aDecoder.decodeObject(forKey: PropertyKey.gender) as? String,
            let tutorGroup = aDecoder.decodeObject(forKey: PropertyKey.tutorGroup) as? String,
            let schoolEmail = aDecoder.decodeObject(forKey: PropertyKey.schoolEmail) as? String,
            let dateOfBirth = aDecoder.decodeObject(forKey: PropertyKey.dateOfBirth) as? String
            else {
                return nil
        }

        self.init(idNumber: idNumber,
                  forename: forename,
                  surname: surname,
                  homeAddress: homeAddress,
                  postcode: postcode,
                  homePhone: homePhone,
                  gender: gender,
                  tutorGroup: tutorGroup,
                  schoolEmail: schoolEmail,
                  dateOfBirth: dateOfBirth
        )
    }


    func encode(with aCoder: NSCoder) {
        aCoder.encode(idNumber, forKey: PropertyKey.idNumber)
        aCoder.encode(forename, forKey: PropertyKey.forename)
        aCoder.encode(surname, forKey: PropertyKey.surname)
        aCoder.encode(homeAddress, forKey: PropertyKey.homeAddress)
        aCoder.encode(postcode, forKey: PropertyKey.postcode)
        aCoder.encode(homePhone, forKey: PropertyKey.homePhone)
        aCoder.encode(gender, forKey: PropertyKey.gender)
        aCoder.encode(tutorGroup, forKey: tutorGroup)
        aCoder.encode(schoolEmail, forKey: PropertyKey.schoolEmail)
        aCoder.encode(dateOfBirth, forKey: PropertyKey.dateOfBirth)
    }
}

var students: [Student] = Student.loadSampleStudents()

print(students)
Student.saveToFile(students: students)

print("Student's were saved")

var reloadedStudents: [Student] = Student.loadFromFile()

print("student's were reloaded")
print(reloadedStudents)

Solution

  • A really simple error, with an easy fix. This line aCoder.encode(tutorGroup, forKey: tutorGroup) needs to be replaced with:

    aCoder.encode(tutorGroup, forKey: PropertyKey.tutorGroup)
    

    So it should look like this:

    func encode(with aCoder: NSCoder) {
        aCoder.encode(idNumber, forKey: PropertyKey.idNumber)
        aCoder.encode(forename, forKey: PropertyKey.forename)
        aCoder.encode(surname, forKey: PropertyKey.surname)
        aCoder.encode(homeAddress, forKey: PropertyKey.homeAddress)
        aCoder.encode(postcode, forKey: PropertyKey.postcode)
        aCoder.encode(homePhone, forKey: PropertyKey.homePhone)
        aCoder.encode(gender, forKey: PropertyKey.gender)
        aCoder.encode(tutorGroup, forKey: PropertyKey.tutorGroup)
        aCoder.encode(schoolEmail, forKey: PropertyKey.schoolEmail)
        aCoder.encode(dateOfBirth, forKey: PropertyKey.dateOfBirth)
    }
    

    I just discovered this by setting breakpoints (inside the guard else closure).