NSUserDefaults with tuples

Hello. I am using NSUserDefaults to save values, e.g.


let defaultsLoad = NSUserDefaults.standardUserDefaults()
// Strings
string1 =   defaultsLoad.stringForKey("String1")
// Arrays
let array1 = defaultsLoad.objectForKey("Array1") as? NSData
        if let array1 = array1
        {
            self.array = NSKeyedUnarchiver.unarchiveObjectWithData(array1) as! [String]
        }


My question is: how do I save and retrieve tuples in swift? I have a tuple variable

(String,String,String)


and an array of these tuples:

[(String,String,String)]


These do not work:


let defaultsLoad = NSUserDefaults.standardUserDefaults()
defaultsLoad.setObject(tuple1, forKey:"Tuple1")
let tupleArrayData = NSKeyedArchiver.archivedDataWithRootObject(tupleArray)
defaultsLoad.setObject(tupleArrayData, forKey: "TupleArray")


let defaultsLoad = NSUserDefaults.standardUserDefaults()
tuple1 = defaultsLoad.objectForKey("Tuple1")
// This one does not show any error. The problem is with saving
let tupleArrayData = defaultsLoad.objectForKey("TupleArray") as? NSData
        if let tupleArrayData = tupleArrayData
        {
            tupleArray = NSKeyedUnarchiver.unarchiveObjectWithData(tupleData) as! [(String,String,String)]
        }


Thanks a lot!

Replies

NSUserDefaults only supports property list types (Strings, numbers, dates, datas, booleans, and arrays/dictionaries of those types), sorry. Similarly, NSKeyedUnarchiver only works on class types, since it's an Objective-C API and tuples cannot be bridged to Objective-C. I don't think there's any way to store tuples in user defaults currently, you'll have to write some code to translate back and forth from something that is supported.

Thanks! I have just noticed I wrote "tulip" instead of tuple. Stupid me! 😁 What was I thinking about?!!


Changed to this:


defaultsLoad.setObject(tuple.0, forKey:"Tuple0")

defaultsLoad.setObject(tuple.1, forKey:"Tuple1")

defaultsLoad.setObject(tuple.2, forKey:"Tuple2")


tuple.0 = defaultsLoad.stringForKey("Tuple0")!

tuple.1 = defaultsLoad.stringForKey("Tuple1")!

tuple.2 = defaultsLoad.stringForKey("Tuple2")!

I thought it was wonderful, personally.

This is what I came up with for an array of tuples.


Saving:


        var arrayTuple0 = [String]()
        var arrayTuple1 = [String]()
        var arrayTuple2 = [String]()
        for component in self.arrayTuple
        {
            let component0 = component.0
            let component1 = component.1
            let component2 = component.2
            arrayTuple0.append(component0)
            arrayTuple1.append(component1)
            arrayTuple2.append(component2)
        }
        let arrayTuple0Data = NSKeyedArchiver.archivedDataWithRootObject(arrayTuple0)
        defaultsLoad.setObject(arrayTuple0Data, forKey: "ArrayTuple0")
        let arrayTuple1Data = NSKeyedArchiver.archivedDataWithRootObject(arrayTuple1)
        defaultsLoad.setObject(arrayTuple1Data, forKey: "ArrayTuple1")
        let arrayTuple2Data = NSKeyedArchiver.archivedDataWithRootObject(arrayTuple2)
        defaultsLoad.setObject(arrayTuple2Data, forKey: "ArrayTuple2")

Retrieving:


        var arrayTuple0 = [String]()
        var arrayTuple1 = [String]()
        var arrayTuple2 = [String]()
        let arrayTuple0Data = defaultsLoad.objectForKey("ArrayTuple0") as? NSData
        if let arrayTuple0Data = arrayTuple0Data
        {
            arrayTuple0 = NSKeyedUnarchiver.unarchiveObjectWithData(arrayTuple0Data) as! [String]
        }
        let arrayTuple1Data = defaultsLoad.objectForKey("ArrayTuple1") as? NSData
        if let arrayTuple1Data = arrayTuple1Data
        {
            arrayTuple1 = NSKeyedUnarchiver.unarchiveObjectWithData(arrayTuple1Data) as! [String]
        }
        let arrayTuple2Data = defaultsLoad.objectForKey("ArrayTuple2") as? NSData
        if let arrayTuple2Data = arrayTuple2Data
        {
            arrayTuple2 = NSKeyedUnarchiver.unarchiveObjectWithData(arrayTuple2Data) as! [String]
        }
        for i in 0..<arrayTuple0.count
        {
            let component0 = arrayTuple0[i]
            let component1 = arrayTuple1[i]
            let component2 = arrayTuple2[i]
            let componentAll = (component0,component1,component2)
            self.arrayTuple.append(componentAll)
        }



And for the tuple, I created a string from tuple (with components separated by a comma).

  let defaultsLoad = NSUserDefaults.standardUserDefaults()
    if tuple != nil
    {
       // Creating a String from the tuple
       let tupleString = tuple!.0 + "," + tuple!.1  + "," + tuple!.2
       defaultsLoad.setObject(tupleString, forKey: "Tuple")
    }


Then, to retrieve the tuple, I retrieved the string, broke it into array and used the array to re-create a tuple.


let tupleString = defaultsLoad.stringForKey("Tuple")
    if let tupleString = tupleString
    {
        let tupleArray = tupleString.componentsSeparatedByString(",")
        tuple = (tupleArray[0],tupleArray[1],tupleArray[2])
    }

You can actually make an array from a tuple pretty easily, if you don't mind using a little ObjC-style dynamism:


func TiptoeThroughTheTulips<T>(tuple: Any) -> [T] {
    return Mirror(reflecting: tuple).children.flatMap { $0.value as? T }
}

let tuple = (1, 2)
let ints: [Int] = TiptoeThroughTheTulips(tuple) // [1, 2]


You'd think that a variant for named tuples would be possible, since Mirror.Child has a .label property, but unfortunately it just seems to be ".0" and ".1" even if proper labels exist. Oh well.

Thank you