Existe-t-il un meilleur moyen de sauvegarder une class personnalisée dans NSUserDefaults que d'encoder et décoder tout avec NSCoder?

Ma class actuelle a environ 50 lignes juste des variables de encoding et de déencoding pour que ma class soit compatible avec NSUserDefaults. Y a-t-il une meilleure façon de gérer cela?

Exemple:

init(coder aDecoder: NSCoder!) { lightEnabled = aDecoder.decodeBoolForKey("lightEnabled") soundEnabled = aDecoder.decodeBoolForKey("soundEnabled") vibrateEnabled = aDecoder.decodeBoolForKey("vibrateEnabled") pulseEnabled = aDecoder.decodeBoolForKey("pulseEnabled") songs = aDecoder.decodeObjectForKey("songs") as! [Song] currentSong = aDecoder.decodeIntegerForKey("currentSong") enableBackgroundSound = aDecoder.decodeBoolForKey("enableBackgroundSound") mixSound = aDecoder.decodeBoolForKey("mixSound") playSoundInBackground = aDecoder.decodeBoolForKey("playSoundInBackground") duckSounds = aDecoder.decodeBoolForKey("duckSounds") BPMBackground = NSKeyedUnarchiver.unarchiveObjectWithData(aDecoder.decodeObjectForKey("BPMBackgorund") as! NSData) as! UIColor! BPMPulseColor = NSKeyedUnarchiver.unarchiveObjectWithData(aDecoder.decodeObjectForKey("BPMPulseColor") as! NSData) as! UIColor! TempoBackGround = NSKeyedUnarchiver.unarchiveObjectWithData(aDecoder.decodeObjectForKey("TempoBackGround") as! NSData) as! UIColor! TempoPulseColor = NSKeyedUnarchiver.unarchiveObjectWithData(aDecoder.decodeObjectForKey("TempoPulseColor") as! NSData) as! UIColor! TimeBackGround = NSKeyedUnarchiver.unarchiveObjectWithData(aDecoder.decodeObjectForKey("TimeBackGround") as! NSData) as! UIColor! TimeStrokeColor = NSKeyedUnarchiver.unarchiveObjectWithData(aDecoder.decodeObjectForKey("TimeStrokeColor") as! NSData) as! UIColor! TextColor = NSKeyedUnarchiver.unarchiveObjectWithData(aDecoder.decodeObjectForKey("TextColor") as! NSData) as! UIColor! } func encodeWithCoder(aCoder: NSCoder!) { aCoder.encodeBool(lightEnabled, forKey: "lightEnabled") aCoder.encodeBool(soundEnabled, forKey: "soundEnabled") aCoder.encodeBool(vibrateEnabled, forKey: "vibrateEnabled") aCoder.encodeBool(pulseEnabled, forKey: "pulseEnabled") aCoder.encodeObject(songs, forKey: "songs") aCoder.encodeInteger(currentSong, forKey: "currentSong") aCoder.encodeBool(enableBackgroundSound, forKey: "enableBackgroundSound") aCoder.encodeBool(mixSound, forKey: "mixSound") aCoder.encodeBool(playSoundInBackground, forKey: "playSoundInBackground") aCoder.encodeBool(duckSounds, forKey: "duckSounds") aCoder.encodeObject(BPMBackground.archivedData(), forKey: "BPMBackground") aCoder.encodeObject(BPMPulseColor.archivedData(), forKey: "BPMPulseColor") aCoder.encodeObject(TempoBackGround.archivedData(), forKey: "TempoBackGround") aCoder.encodeObject(TempoPulseColor.archivedData(), forKey: "TempoPulseColor") aCoder.encodeObject(TimeBackGround.archivedData(), forKey: "TimeBackGround") aCoder.encodeObject(TimeStrokeColor.archivedData(), forKey: "TimeStrokeColor") aCoder.encodeObject(TextColor.archivedData(), forKey: "TextColor") } 

Vous devriez créer une structure ou une énumération pour organiser vos keys, car votre façon de faire est sujette aux fautes de frappe. Juste le mettre juste au-dessus de votre class

 enum Key: Ssortingng { case allSettings case lightEnabled case soundEnabled } 

et que d'appeler simplement les touches comme si

 ...forKey: Key.lightEnabled.rawValue) 

Maintenant, en ce qui concerne votre question, je faisais face au même problème avec mon jeu en essayant d'save des propriétés pour 40 niveaux (bestTimes, niveau de déblocage de niveau, etc). J'ai d'abord fait ce que tu as essayé et c'était de la pure folie.

J'ai fini par utiliser des arrays / dictionarys ou même des arrays de dictionarys pour mes données, ce qui réduit mon code de 80%.

Ce qui est également intéressant à propos de cela, c'est que vous devez sauvegarder quelque chose comme des bols LevelUnlock, cela rendra votre vie tellement plus facile plus tard. Dans mon cas, j'ai un button UnlockAllLevels, et maintenant je peux juste faire une boucle dans mon dictionary / tableau et mettre à jour / vérifier les booleans levelUnlock dans quelques lignes de code. C'est bien mieux que d'avoir des instructions massives if-else ou switch pour vérifier chaque propriété individuellement.

Par exemple

  var settingsDict = [ Key.lightEnabled.rawValue: false, Key.soundEnabled.rawValue: false, ... ] 

Que dans la méthode du décodeur, vous dites cela

Note: De cette façon, vous devrez append de nouvelles valeurs à SettingsDict et, à l'application suivante, ces valeurs ne seront pas supprimées, car vous ne remplacez pas le dictionary entier par celui sauvegardé, vous ne mettez à jour que les valeurs déjà existantes. exister.

  // If no saved data found do nothing if var savedSettingsDict = decoder.decodeObjectForKey(Key.allSettings.rawValue) as? [Ssortingng: Bool] { // Update the dictionary values with the previously saved values savedSettingsDict.forEach { // If the key does not exist anymore remove it from saved data. guard settingsDict.keys.contains($0) else { savedSettingsDict.removeValue(forKey: $0) return } settingsDict[$0] = $1 } } 

Si vous utilisez plusieurs dictionarys, votre méthode de déencoding deviendra de plus en plus désordonnée et vous répéterez également beaucoup de code. Pour éviter cela, vous pouvez créer une extension de NSCoder en utilisant des generics.

  extension NSCoder { func decodeObject<T>(_ object: [Ssortingng: T], forKey key: Ssortingng) -> [Ssortingng: T] { guard var savedData = decodeObject(forKey: key) as? [Ssortingng: T] else { return object } var newData = object savedData.forEach { guard object.keys.contains($0) else { savedData[$0] = nil return } newData[$0] = $1 } return newData } } 

et que vous pouvez écrire cela dans la méthode du décodeur pour chaque dictionary.

 settingsDict = aDecoder.decodeObject(settingsDict, forKey: Key.allSettings.rawValue) 

Votre méthode d'encodeur ressemblerait à ceci.

  encoder.encodeObject(settingsDict, forKey: Key.allSettings.rawValue) 

Dans votre jeu / application, vous pouvez les utiliser comme si

 settingsDict[Key.lightEnabled.rawValue] = true if settingsDict[Key.lightEnabled.rawValue] == true { /// light is turned on, do something } 

De cette façon, il est également très facile d'intégrer le stockage iCloud KeyValue (il suffit de créer un dictionary iCloud), principalement parce qu'il est si facile de sauvegarder et de comparer beaucoup de valeurs avec très peu de code.

METTRE À JOUR:

Pour rendre l'appel un peu plus facile, j'aime créer des getters / setters de commodité dans la class GameData. Cela a l'avantage que vous pouvez plus facilement appeler ces propriétés dans votre projet (comme votre ancienne méthode), mais votre méthode de encoding / déencoding restra compact. Vous pouvez également faire des choses telles que la mise en boucle pour comparer les valeurs.

  var isLightEnabled: Bool { get { return settingsDict[Key.lightEnabled.rawValue] ?? false } set { settingsDict[Key.lightEnabled.rawValue] = newValue } } var isSoundEnabled: Bool { get { return settingsDict[Key.soundEnabled.rawValue] ?? false } set { settingsDict[Key.soundEnabled.rawValue] = newValue } } 

et que vous pouvez les appeler comme des propriétés normales.

 isLightEnabled = true if isLightEnabled { /// light is turned on, do something }