web-dev-qa-db-fra.com

Swift 3 enregistrement et récupération d'objet personnalisé à partir de userDefaults

J'ai ceci dans Playground en utilisant Swift 3, Xcode 8.0:

import Foundation
class Person: NSObject, NSCoding {
    var name: String
    var age: Int
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
    required convenience init(coder aDecoder: NSCoder) {
        let name = aDecoder.decodeObject(forKey: "name") as! String
        let age = aDecoder.decodeObject(forKey: "age") as! Int
        self.init(
            name: name,
            age: age
        )
    }
    func encode(with aCoder: NSCoder) {
        aCoder.encode(name, forKey: "name")
        aCoder.encode(age, forKey: "age")
    }
}

créer un tableau de personne

let newPerson = Person(name: "Joe", age: 10)
var people = [Person]()
people.append(newPerson)

encoder le tableau

let encodedData = NSKeyedArchiver.archivedData(withRootObject: people)
print("encodedData: \(encodedData))")

enregistrer dans les paramètres par défaut

let userDefaults: UserDefaults = UserDefaults.standard()
userDefaults.set(encodedData, forKey: "people")
userDefaults.synchronize()

vérifier

print("saved object: \(userDefaults.object(forKey: "people"))")

récupérer de userDefaults

if let data = userDefaults.object(forKey: "people") {
    let myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: data as! Data)
    print("myPeopleList: \(myPeopleList)")
}else{
    print("There is an issue")
}

il suffit de vérifier les données archivées

if let myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: encodedData){
   print("myPeopleList: \(myPeopleList)")
}else{
   print("There is an issue")
}

Je ne parviens pas à enregistrer correctement l'objet de données dans userDefaults. De plus, la vérification au bas de l'écran crée l'erreur "Erreur irrécupérable: erreur inopinée trouvée lors du décompression d'une valeur facultative". La ligne "check" indique également que l'objet enregistré est nil. Est-ce une erreur dans le NSCoder de mon objet?

60
user773881

Swift 4 Note

Vous pouvez à nouveau enregistrer/tester vos valeurs dans un terrain de jeu.


Swift

UserDefaults doivent être testés dans un projet réel. Remarque: Pas besoin de forcer la synchronisation. Si vous souhaitez tester le codage/décodage dans un terrain de jeu, vous pouvez enregistrer les données dans un fichier Plist du répertoire de documents à l’aide de l’archiveur à clé. Vous devez également résoudre certains problèmes dans votre classe:

class Person: NSObject, NSCoding {
    let name: String
    let age: Int
    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")
    }
}

Essai:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // setting a value for a key
        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")

        // retrieving a value for a key
        if let data = UserDefaults.standard.data(forKey: "people"),
            let myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: data) as? [Person] {
            myPeopleList.forEach({print( $0.name, $0.age)})  // Joe 10
        } else {
            print("There is an issue")
        }
    }
}
89
Leo Dabus
let age = aDecoder.decodeObject(forKey: "age") as! Int

Cela a été changé pour Swift 3; cela ne fonctionne plus pour les types de valeur. La syntaxe correcte est la suivante:

let age = aDecoder.decodeInteger(forKey: "age")

Il existe des fonctions de décodage associées ... () pour différents types:

let myBool = aDecoder.decodeBoolean(forKey: "myStoredBool")
let myFloat = aDecoder.decodeFloat(forKey: "myStoredFloat")

Edit: Liste complète de toutes les fonctions possibles de decodeXXX dans Swift

Modifier:

Autre remarque importante: si vous avez déjà enregistré des données encodées avec une version antérieure de Swift, ces valeurs doivent être décodées à l’aide de decodeObject (), cependant une fois que vous recodez les données en utilisant encode (...), elles ne peuvent plus être décodées avec decodeObject () s'il s'agit d'un type de valeur. Par conséquent, la réponse de Markus Wyss vous permettra de gérer le cas où les données ont été encodées en utilisant la version Swift:

self.age = aDecoder.decodeObject(forKey: "age") as? Int ?? aDecoder.decodeInteger(forKey: "age")
49
ibuprofane

Essaye ça:

self.age = aDecoder.decodeObject(forKey: "age") as? Int ?? aDecoder.decodeInteger(forKey: "age")
11
mswyss

Dans Swift 4:

Vous pouvez utiliser Codable pour enregistrer et récupérer un objet personnalisé à partir des défauts utilisateur. Si vous le faites fréquemment, vous pouvez ajouter une extension et l’utiliser comme ci-dessous.

extension UserDefaults {

   func save<T:Encodable>(customObject object: T, inKey key: String) {
       let encoder = JSONEncoder()
       if let encoded = try? encoder.encode(object) {
           self.set(encoded, forKey: key)
       }
   }

   func retrieve<T:Decodable>(object type:T.Type, fromKey key: String) -> T? {
       if let data = self.data(forKey: key) {
           let decoder = JSONDecoder()
           if let object = try? decoder.decode(type, from: data) {
               return object
           }else {
               print("Couldnt decode object")
               return nil
           }
       }else {
           print("Couldnt find key")
           return nil
       }
   }

}

Votre classe doit suivre Codable. C'est juste un typealias à la fois pour le protocole encodable et décodable.

class UpdateProfile: Codable {
  //Your stuffs
}

Utilisation:

let updateProfile = UpdateProfile()

//To save the object
UserDefaults.standard.save(customObject: updateProfile, inKey: "YourKey")

//To retrieve the saved object
let obj = UserDefaults.standard.retrieve(object: UpdateProfile.self, fromKey: "YourKey")

Pour plus de types d'encodage et de décodage personnalisés, veuillez consulter la documentation d'Apple .

6
Bishow Gurung