web-dev-qa-db-fra.com

NSKeyedArchiver ne fonctionne pas dans Swift 3 (XCode 8)

J'ai migré mon projet vers Swift 3 et NSKeyedArchiver ne fonctionne pas. J'ai en fait une erreur d'exécution en essayant de décoder un objet comme celui-ci:

let startDayTime = aDecoder.decodeObject(forKey: Key.startDayTime) as! Int

Cela fonctionnait parfaitement dans Swift 2.2 dans Xcode 7.3. Quelqu'un at-il fait face à de tels problèmes?

P.S. J'ai cette erreur sur Simulator et Device.

UPDATE: J'ai résolu ce problème en utilisant decodeInteger(forKey key: String) au lieu de decodeObject(forKey key: String). Pour une raison quelconque, AnyObject n’a pas recours à Integer dans Swift 3 contrairement à Swift 2.2

16
Arthur Shkil

Il semble que cela ne se produise que sur la limite de mise à jour Swift 2 à Swift 3 lorsqu'un blob NSData archivé avec une variable NSKeyedArchiver dans Swift 2 est ouvert avec une variable NSKeyedUnarchiver dans Swift 3. Je suppose que sur Swift 2, les variables Bool et IntNSNumber, mais dans Swift 3, ils sont codés en tant que types Bool et Int bruts. Je crois que le test suivant confirme cette affirmation:

Cela fonctionne dans Swift 3 pour désarchiver une Bool encodée dans Swift 2, mais renvoie nil si le booléen a été encodé dans Swift 3:

let visible = aDecoder.decodeObject(forKey: "visible") as? Bool

Cela fonctionne dans Swift 3 pour désarchiver une Bool encodée dans Swift 3, mais se bloque si le booléen a été encodé dans Swift 2:

let visible = aDecoder.decodeBool(forKey: "visible")

Ma solution est:

let visible = aDecoder.decodeObject(forKey: "visible") as? Bool ?? aDecoder.decodeBool(forKey: "visible")
16
James Kuang

UPDATE: J'ai résolu ce problème en utilisant decodeInteger (forKey key: String) au lieu de decodeObject (forKey key: String). Pour une raison quelconque, AnyObject n’a pas recours à Integer dans Swift 3 contrairement à Swift 2.2

7
Arthur Shkil

Voici la solution:

class Person: NSObject, NSCoding {
let name: String
let age: Int
required init(name: String, age: Int) {
    self.name = name
    self.age = age
}
required init(coder decoder: NSCoder) {
    self.name = decoder.decodeObject(forKey: "name") as? String ?? ""
    self.age = decoder.decodeInteger(forKey: "age")
}

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

Comment utiliser:

class ViewController: UIViewController {
override func viewDidLoad() {
    super.viewDidLoad()
    let newPerson = Person(name: "Joe", age: 10)
    var people = [Person]()
    people.append(newPerson)
    let encodedData = NSKeyedArchiver.archivedData(withRootObject: people)
    UserDefaults.standard().set(encodedData, forKey: "people")
    if let data = UserDefaults.standard().data(forKey: "people"),
        myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: data) as? [Person] {
        myPeopleList.forEach({print( $0.name, $0.age)})  // Joe 10
    } else {
        print("There is an issue")
    }
}
override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}}

Référence: Swift 3 enregistrer et récupérer un objet personnalisé à partir de userDefaults

3
Amit Soni

Swift 3:

J'adopte la méthode {double point d'interrogation} _ ( ?? ) et fonctionne comme un charme sur une seule ligne.

Si val n'est pas nul, il est décompressé et la valeur renvoyée. Si elle est nulle, aDecoder.decodeInteger(forKey: "val") est retourné:

self.val = aDecoder.decodeObject(forKey: "val") as? Int ?? aDecoder.decodeInteger(forKey: "val")
1
Alessandro Ornano