Reputation: 31323
I'm using a tuple to store something like this.
var accessLavels: (hasInventoryAccess: Bool, hasPayrolAccess: Bool)
accessLavels = (hasInventoryAccess: true, hasPayrolAccess: false)
Now I want to save it in NSUserDefaults
.
NSUserDefaults.standardUserDefaults().setValue(accessLavels, forKey: "AccessLevelKey")
NSUserDefaults.standardUserDefaults().synchronize()
But I get the following error.
Type '(hasInventoryAccess: Bool, hasPayrolAccess: Bool)' does not conform to protocol 'AnyObject'
How can I resolve this issue? If its impossible, then any other suggestions to save a tuple is welcome.
Thank you.
Upvotes: 7
Views: 6642
Reputation: 12272
Saving tuples to prefs is a very common thing.
The nature of tuples is that they are simple, sloppy, untyped, unnamed, fast and light versions of arrays. They're a "shoddy array".
Hence the best idiom and most natural way to save tuples is as a shoddy array.
If the tuple types are all the same and simple, definitely use a shoddy array.
If the tuple types are mixed but easily "saved as" a type, definitely use a shoddy array.
Here's an example of the completely common thing of saving a tuple of "sort column and sort direction", which every app uses constantly.
///For a given screen name save the recent sort col and sense.
func set(forScreen: String, sortColumnSense: (Int, Bool)) {
UserDefaults.standard.set(
[sortColumnSense.0, (sortColumnSense.1 ? 1 : 0)],
forKey: "sortTup_\(forScreen.suffix(64))")
}
///For a given screen name recover the recent sort col sense
func getSortColumnSense(forScreen: String) -> (Int, Bool)? {
let quasi = UserDefaults.standard.array(forKey: "sortTup_\(forScreen.suffix(64))")
guard let quasi,
quasi.count == 2,
let tup = quasi as? [Int]
else { return nil }
return (tup[0], (tup[1] == 0 ? false : true))
}
This completely avoids the complicated issue of consistency & cleanup if you use N different prefs keys, and the simple ad-hoc nature of storing/checking centralizes all the checking logic to SSOT so that it's impossible for any team member to be on a different page and is completely type migration safe, which is otherwise a huge amount of code if you use one of the other three approaches.
Upvotes: 0
Reputation: 607
I'ts an year old question but still:
let accesLvl : [String:AnyObject] = ["hasInventoryAcces":true, "hasPayrolAccess":false]
NSUserDefaults.standardUserDefaults().setObject(accesLvl, forKey: "accesLevel")
In case you save only bools, let accesLvl : [String:Bool]
is a better option.
In case I don't get something (I'm fairly new to Swift and programming altogether), what would be the benefit of using a "tuple" over a Dictionary, or even a struct
Upvotes: 0
Reputation: 1193
I had a tuple with 3 values.
The following code was used for saving the tuple. Basically, I created a string from tuple (with components separated by a comma).
let defaultsLoad = NSUserDefaults.standardUserDefaults()
if appSingleton.runwayDestShared != nil
{
// Creating a String from the tuple
let runwayDestString = appSingleton.runwayDestShared!.0 + "," + appSingleton.runwayDestShared!.1 + "," + appSingleton.runwayDestShared!.2
defaultsLoad.setObject(runwayDestString, forKey: "RunwayDest")
}
To retrieve the tuple, I retrieved the string, broke it into array and used the array to re-create a tuple.
let runwayDestString = defaultsLoad.stringForKey("RunwayDest")
if let runwayDestString = runwayDestString
{
let runwayDestArray = runwayDestString.componentsSeparatedByString(",")
appSingleton.runwayDestShared = (runwayDestArray[0],runwayDestArray[1],runwayDestArray[2])
}
Upvotes: 1
Reputation: 4016
In the documentation, I don't see any method -setValue
.
Based on the docs, NSUserDefaults is able to save NSString, NSNumber, NSData, NSDictionary, NSArray, NSData and NSDate only. Here is the link:
NSUserDefaults Class Reference.
So, you are not able to save tuple here. And -setValue
is from NSKeyValueCoding
protocol.
Upvotes: -1
Reputation: 5334
I encountered a similar scenario trying to encode a tuple with NSCoder
. The way I am solving it is by manually converting the tuple to a Dictionary
. This is not a great solution as the keys need to be changed in several places if the tuple ever changes.
I had a nested enum in my tuple and gave it a base type (String) from which I converted the raw value. It was a little extra work but thankfully yours is only primitives.
# SerializeableTuple.swift
typealias AccessTuple = (hasInventoryAccess: Bool, hasPayrolAccess: Bool)
typealias AccessDictionary = [String: Bool]
let InventoryKey = "hasInventoryAccess"
let PayrollKey = "hasPayrollAccess"
func serializeTuple(tuple: AccessTuple) -> AccessDictionary {
return [
InventoryKey : tuple.hasInventoryAccess,
PayrollKey : tuple.hasPayrolAccess
]
}
func deserializeDictionary(dictionary: AccessDictionary) -> AccessTuple {
return AccessTuple(
dictionary[InventoryKey] as Bool!,
dictionary[PayrollKey] as Bool!
)
}
# Encoding / Decoding
var accessLavels: AccessTuple = (hasInventoryAccess: true, hasPayrolAccess: false)
// Writing to defaults
let accessLevelDictionary = serializeTuple(accessLavels)
NSUserDefaults.standardUserDefaults().setObject(accessLevelDictionary, forKey: "AccessLevelKey")
// Reading from defaults
let accessDic = NSUserDefaults.standardUserDefaults().dictionaryForKey("AccessLevelKey") as AccessDictionary
let accessLev = deserializeDictionary(accessDic)
Upvotes: 8
Reputation: 236498
You can store Bool, Float, Int, Object, Double or URL but not a Tuple. So you have two options, save two only hasPayrolAccess and hasPayrolAccess Bool values:
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "hasInventoryAccess")
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "hasPayrolAccess")
let hasInventoryAccess = NSUserDefaults.standardUserDefaults().boolForKey("hasInventoryAccess")
println(hasInventoryAccess)
let hasPayrolAccess = NSUserDefaults.standardUserDefaults().boolForKey("hasPayrolAccess")
println(hasPayrolAccess)
Or save it using an Array of Bool:
var accessLavels = [true,false]
println(accessLavels)
NSUserDefaults.standardUserDefaults().setValue(accessLavels, forKey: "accessLavels")
if let loadAccessLavels = NSUserDefaults.standardUserDefaults().arrayForKey("accessLavels") as? [Bool] {
if let hasInventoryAccess = loadAccessLavels.first {
println(hasInventoryAccess)
}
if let hasPayrolAccess = loadAccessLavels.last {
println(hasPayrolAccess)
}
}
Upvotes: 1