Charles B.
Charles B.

Reputation: 181

How to save an array of classes in swift code

I am trying to save an array of classes and here is my code so far:

The array:

var person1 = person(name: "Bob", age: 22)
var person2 = person(name: "John", age: 10)
var array = [person1, person2]

The custom class:

import UIKit

class person: NSObject, NSCoding {

    var name : String
    var age : Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    required init (coder aDecoder: NSCoder) {
        name = (aDecoder.decodeObject(forKey: "name") as? String)!
        age = (aDecoder.decodeObject(forKey: "age") as? Int)!
    }

   func encode(with aCoder: NSCoder) {
        aCoder.encode(name, forKey: "name")
        aCoder.encode(age, forKey: "age")
    }
}

To save the array:

let SavedData = NSKeyedArchiver.archivedData(withRootObject: array)

let defaults = UserDefaults.standard

defaults.set(SavedData, forKey: "myPeople")

To load the array:

let saveddata = UserDefaults.standard.object(forKey: "myPeople") as? Data

if saveddata != nil{
    array = (NSKeyedUnarchiver.unarchiveObject(with: saveddata!) as? [person])!
}

The error is at the line age = (aDecoder.decodeObject(forKey: "age") as? Int)!

Unknown class iewripple in Interface Builder file.

Upvotes: 1

Views: 124

Answers (2)

vadian
vadian

Reputation: 285039

The error occurs because you are encoding an Int but decoding an object which fails.

There is a dedicated method decodeInteger(forKey.

Please conform to the naming convention that class names start with a capital letter and variable names start with a lowercase letter.

class Person: NSObject, NSCoding {

To solve the problem change the init(coder method to

required init(coder aDecoder: NSCoder) {
    name = aDecoder.decodeObject(forKey: "name") as! String
    age = aDecoder.decodeInteger(forKey: "age")
}

Forced unwrapping the string is 100% safe since the value is always encoded as non-optional.


Save the array

let savedData = NSKeyedArchiver.archivedData(withRootObject: array)
UserDefaults.standard.set(savedData, forKey: "myPeople")

Load the array

if let savedData = UserDefaults.standard.object(forKey: "myPeople") as? Data {
    array = NSKeyedUnarchiver.unarchiveObject(with: savedData) as! [Person]
}

Forced unwrapping is also safe in this case if the optional binding as? Data succeeds.

Upvotes: 1

Lawliet
Lawliet

Reputation: 3499

You should not force unwrapping. Try getting

required init (coder aDecoder: NSCoder) {
    name = (aDecoder.decodeObject(forKey: "name") as? String)!
    age = (aDecoder.decodeObject(forKey: "age") as? Int)!
}

replaced by

required init (coder aDecoder: NSCoder) {
    name = aDecoder.decodeObject(forKey: "name") as? String ?? ""
    age = aDecoder.decodeInteger(forKey: "age")
}

Get

let saveddata = UserDefaults.standard.object(forKey: "myPeople") as? Data
if saveddata != nil{
    array = (NSKeyedUnarchiver.unarchiveObject(with: saveddata!) as? [person])!
}

replaced by

if let data = UserDefaults.standard.object(forKey: "myPeople"),
    let array = NSKeyedUnarchiver.unarchiveObject(with: data as! Data) as? [person] {
    persons = array
}

I have tested. The code is working fine.

let person1 = person(name: "Bob", age: 22)
let person2 = person(name: "John", age: 10)
var persons = [person1, person2]

// Save data
let data = NSKeyedArchiver.archivedData(withRootObject: persons)
let defaults = UserDefaults.standard
defaults.set(data, forKey: "myPeople")

// Load data
if let data = UserDefaults.standard.object(forKey: "myPeople"),
    let array = NSKeyedUnarchiver.unarchiveObject(with: data as! Data) as? [person] {
    persons = array
}

Additionally, it is conventional to capitalise the first letter of a class name. You can check this out for naming convention.

Upvotes: 1

Related Questions