Array, Dictionary et NSCoding

Bonjour à toutes et à tous
Je viens d'experimenter avec Swift, en refaisant plusieurs de mes classes du dernier projet.
Parmi ces classes, j'ai besoin d'un OrderedDictionary, donc je l'ai bidouillé avec quelques exemples et j'ai abouti avec ce code :
struct OrderedDictionary<keyType : protocol<Hashable, Printable>, valueType>
{
var keys: Array<keyType> = []
var values: Dictionary<keyType, valueType> = [:]
subscript(key: keyType) -> valueType?
{
get
{
return self.values[key]
}
set(newValue)
{
if newValue == nil
{
if let index = find(keys, key)
{
self.keys.removeAtIndex(index)
self.values.removeValueForKey(key)
}
return
}
let oldValue = self.values.updateValue(newValue!, forKey:key)
if oldValue == nil
{
self.keys.append(key)
}
}
}
subscript(index: Int) -> valueType?
{
get
{
if (index < 0) || (index >= keys.endIndex)
{
return nil
}
let key = keys[index]
return values[key]!
}
}
init() {}
var description: String
{
var result = "{\n"
for i in 0 ..< self.keys.count
{
let key = self.keys[i]
result += "[\(i)]: \(key) => \(self[key])\n"
}
result += "}"
return result
}
}
Bon ! mais l'OrderedDictionary fait partie d'une classe qui doit être archivé comme partie d'une hiérarchie d'objets.
Mais OrderedDictionary est un struct et, dans cette hiérarchie, keyType et valueType sont les enums ; et on ne peut archiver que les classes marquées comme @objc ou qui héritent de NSObject.
Après beaucoup de recherches, j'ai trouvé que le code suivant, dans la classe qui contient le dictionnaire, va assez bien.
required convenience init(coder aDecoder: NSCoder)
{
self.init()
let keys = aDecoder.decodeObjectForKey("availableFuelTypeKeys") as NSArray
let values = aDecoder.decodeObjectForKey("availableFuelTypeValues") as NSDictionary
for keyItem in keys
{
let key = keyItem as NSNumber
let fuelType = FuelType(rawValue: key.integerValue)
availableFuelTypes.keys.append(fuelType!)
let value = values[key] as NSNumber
let availability = ServiceAvailability(rawValue: value.integerValue)
availableFuelTypes.values[fuelType!] = availability
}
}
func encodeWithCoder(aCoder: NSCoder)
{
let keys: NSMutableArray = NSMutableArray(capacity: availableFuelTypes.keys.count)
for key in availableFuelTypes.keys
{
keys.addObject(key.rawValue)
}
let values: NSMutableDictionary = NSMutableDictionary(capacity: availableFuelTypes.values.count)
for (key, value) in availableFuelTypes.values
{
values[key.rawValue] = value.rawValue
}
aCoder.encodeObject(keys, forKey: "availableFuelTypeKeys")
aCoder.encodeObject(values, forKey: "availableFuelTypeValues")
}
C'est loin d'être joli - je voudrais vous demander si vous avez d'autres idées ?
Réponses
Alors pour rendre les types Swift plus facile à intégrer à Cocoa j'utilise ce genre d'implémentation. Tout ça vient de mon expérience et ça peut servir dans bien des cas. J'ai fait un code complet d'exemple :
Dans ton cas qui est plus précis et dans la mesure ou un Array<keyType> ne répond pas au protocol AnyObject on est obligé de le sérialiser autrement, par le biais de NSData. (Ce code n'est pas de moi, je l'ai un peu modifié, l'utilise dans mes projets et il vient de https://gist.github.com/nubbel/5b0a5cb2bf6a2e353061) :
Normalement avec ça tu devrais avoir assez pour extrapoler et faire un peu tout ce que tu veux faire