Сохранить словарь пользовательских объектов в Swift с помощью UserDefaults

Я пишу приложение для работы с контактами и пытаюсь сохранить информацию с помощью UserDefaults, но получаю SIGABRT. Вот мой код, что я делаю не так?

class Contact{
var name = String()
var phone = String()

init(name: String, phone: String){
    self.name=name
    self.phone=phone
}
required init(coder decoder: NSCoder){
    self.name = (decoder.decodeObject(forKey: "name") as? String)!
    self.phone = (decoder.decodeObject(forKey: "phone") as? String)!
}
func encode(with coder: NSCoder){
    coder.encode(name, forKey: "name")
    coder.encode(phone, forKey: "phone")
}
}

Создание контакта в ViewDidLoad просто для теста:

        let userDefaults = UserDefaults.standard
var contactDictionary = Dictionary<String, [Contact]>()

  override func viewDidLoad() {
    super.viewDidLoad()
    contactDictionary["A"] = [Contact(name: "Annabel",phone: "000")]

    let encodedData = NSKeyedArchiver.archivedData(withRootObject: contactDictionary)
    userDefaults.set(encodedData, forKey: "contactDictionary")
    userDefaults.synchronize()
    if let data = userDefaults.data(forKey: "contactDictionary"){
        print("yep")
        contactDictionary = (NSKeyedUnarchiver.unarchiveObject(with: data) as? [String : [Contact]])!
    }
    else{
        print("nope")
    }
}

person Candis Mayer    schedule 15.12.2017    source источник
comment
Вам нужно сделать ваш класс NSCoding совместимым и наследовать от NSObject. Измените объявление Контакта на class Contact: NSObject, NSCoding {. И, кстати, условное приведение, чтобы принудительно развернуть его позже, бессмысленно. decoder.decodeObject(forKey: "name") as! String   -  person Leo Dabus    schedule 16.12.2017
comment
Что касается принудительной развертки, здесь то же самое NSKeyedUnarchiver.unarchiveObject(with: data) as! [String : [Contact]]   -  person Leo Dabus    schedule 16.12.2017
comment
Не помогает, получаю тот же SIGABRT, а он пишет: Неопознанный селектор - [Контакты.Замена контактаОбъектДляKeyedArchiver:]   -  person Candis Mayer    schedule 16.12.2017
comment
Я проверил это здесь, и это работает. Отредактируйте свой вопрос и добавьте свой фактический код   -  person Leo Dabus    schedule 16.12.2017
comment
Спасибо, попробую разобраться   -  person Candis Mayer    schedule 16.12.2017
comment
проверьте мой опубликованный ответ   -  person Leo Dabus    schedule 16.12.2017


Ответы (1)


Вам нужно сделать ваш класс совместимым с NSCoding и наследовать от NSObject. Измените объявление Contact на class Contact: NSObject, NSCoding {. И, кстати, условное приведение, чтобы принудительно развернуть его позже, бессмысленно. decoder.decodeObject(forKey: "name") as! String

class Contact: NSObject, NSCoding {
    var name = String()
    var phone = String()
    init(name: String, phone: String){
        self.name=name
        self.phone=phone
    }
    required init(coder decoder: NSCoder){
        self.name = decoder.decodeObject(forKey: "name") as! String
        self.phone = decoder.decodeObject(forKey: "phone") as! String
    }
    func encode(with coder: NSCoder){
        coder.encode(name, forKey: "name")
        coder.encode(phone, forKey: "phone")
    }
}

Тестирование:

let contactDictionary = ["A":[Contact(name: "Annabel",phone: "000")]]

let encodedData = NSKeyedArchiver.archivedData(withRootObject: contactDictionary)
UserDefaults.standard.set(encodedData, forKey: "contactDictionary")

if let data = UserDefaults.standard.data(forKey: "contactDictionary") {
    print("yep")
    let contactDictionary2 = NSKeyedUnarchiver.unarchiveObject(with: data) as! [String : [Contact]]
}
else{
    print("nope")
}
person Leo Dabus    schedule 15.12.2017