Vik Singh
Vik Singh

Reputation: 1603

NSCoding swift dictionary with swift tuple as values

I have a Swift dictionary with keys as Strings and values as Swift tuples. I would like to send this dictionary over to the other device so I need to implement NSCoding on this dictionary. Can anybody help me as to how can I achieve that. Following is my dictionary code.

class STCTruthDict: NSObject, NSCoding, SequenceType {

   typealias IpRelationshipTuple = (String, String?)

   private var truthDict: [String : IpRelationshipTuple] = [ : ]

   subscript(key: String) -> IpRelationshipTuple? {
     get {
       return self.truthDict[key]
     }
     set {
       truthDict[key] = newValue
     }
   }

   // MARK: - Initializers

   override init() {
     super.init()
   }

   required init(coder aDecoder: NSCoder) {
     self.truthDict = aDecoder.decodeObjectForKey("truthDict") as! [String : IpRelationshipTuple]
     let key = aDecoder.decodeObjectForKey("user_id") as? String
     let ip = aDecoder.decodeObjectForKey("tupleIp") as? String
     let groupId = aDecoder.decodeObjectForKey("tupleGroupId") as? String
   }

   func encodeWithCoder(aCoder: NSCoder) {
      for (key, tuple) in self.truthDict {
            aCoder.encodeObject(key, forKey: "user_id")
            aCoder.encodeObject(tuple.Ip, forKey: "tupleIp")
            aCoder.encodeObject(tuple.groupId, forKey: "tupleGroupId")
        }       
   }

   func generate() -> DictionaryGenerator <String, IpRelationshipTuple> {
    return self.truthDict.generate()
   }

}

Upvotes: 3

Views: 3129

Answers (1)

Erik
Erik

Reputation: 12868

The problem you are running into is that tuples are structs and not class (object) types. You'll notice this issue when you try to do the following:

if let dictionary = aDecoder.decodeObjectForKey("truthDict") as? [String : RelationshipType] { ... }

This issues unfortunately comes up a bit when trying to deal with value types in Swift. To get around this limitation, you can box your value types using a generic Box class like so:

class Box<T>
{
    var value : T?

    init(_ value: T?)
    {
        self.value = value
    }
}

With the Box class, you can use this to encode and decode your dictionary tuples:

class TruthDictionary : NSObject, NSCoding, SequenceType
{
    typealias RelationshipType = (ip: String, groupId: String?)

    private var dictionary = [String : RelationshipType]()

    subscript(key: String) -> RelationshipType?
    {
        get { return self.dictionary[key] }
        set { self.dictionary[key] = newValue }
    }

    // MARK: - Initializers

    override init()
    {
        super.init()
    }

    // MARK: - NSCoding

    required init(coder aDecoder: NSCoder)
    {
        // Unbox each tuple from the decoded dictionary
        if let boxedDictionary = aDecoder.decodeObjectForKey("truthDict") as? [String : Box<RelationshipType>]
        {
            for (key, boxedTuple) in boxedDictionary
            {
                self.dictionary[key] = boxedTuple.value
            }
        }
    }

    func encodeWithCoder(aCoder: NSCoder)
    {
        var boxedDictionary = [String: Box<RelationshipType>]()

        // Box each tuple to the dictionary to be encoded
        for (key, tuple) in self.dictionary
        {
            boxedDictionary[key] = Box(tuple)
        }

        aCoder.encodeObject(boxedDictionary, forKey: "truthDict")
    }

    // MARK: - SequenceType

    func generate() -> DictionaryGenerator<String, RelationshipType>
    {
        return dictionary.generate()
    }
}

Upvotes: 4

Related Questions