Reputation: 3868
I'm trying to store an array of custom structs.
In my attempt below I get a run-time error in the second line of saveSampleArrayOfQuerySettings() complaining about casting to AnyObject.
struct QuerySettings {
// let ARRAY_INDEX_WHERE_TO_SAVE_STRUCT = 0
let QUERY_SETTINGS_KEY = "querysettings"
let defaults = NSUserDefaults.standardUserDefaults()
private var _includeCompletedReminders: Bool = false // Default value
var includeCompletedReminders: Bool {
get {
// If value has been set before then use it otherwise retrieve it.
return _includeCompletedReminders
} set (newVal) {
_includeCompletedReminders = newVal
// saveSettings(self, index: ARRAY_INDEX_WHERE_TO_SAVE_STRUCT, saveKey: QUERY_SETTINGS_KEY)
}
}
private var _includeRemindersWithNoDueDate: Bool = true
var includeRemindersWithNoDueDate: Bool {
get {
return _includeRemindersWithNoDueDate
} set (newVal) {
_includeRemindersWithNoDueDate = newVal
// saveSettings(self, index: ARRAY_INDEX_WHERE_TO_SAVE_STRUCT, saveKey: QUERY_SETTINGS_KEY)
}
}
}
func saveSampleArrayOfQuerySettings(saveKey: String) {
let sampleArray = [QuerySettings(), QuerySettings()]
// Persist
let archivedSettingsArray = NSKeyedArchiver.archivedDataWithRootObject(sampleArray as! AnyObject)
// RUNTIME ERROR AT PREVIOUS LINE: "Could not cast value of type 'Swift.Array<RemindersPro.QuerySettings>' (0x112df10d8) to 'Swift.AnyObject' (0x1169d6018)."
NSUserDefaults.standardUserDefaults().setObject(archivedSettingsArray, forKey: saveKey)
NSUserDefaults.standardUserDefaults().synchronize()
// For Testing Purposes - Load the saved settings
if let retrievedArchivedSettingsArray : AnyObject = NSUserDefaults.standardUserDefaults().objectForKey(saveKey) {
if let settingsArray : AnyObject = NSKeyedUnarchiver.unarchiveObjectWithData(retrievedArchivedSettingsArray as! NSData) {
let arr = settingsArray as! [QuerySettings]
print(arr.first)
}
}
}
saveSampleArrayOfQuerySettings("saveKey")
I wonder whether I need to encode my struct. I checked out these posts but oddly couldn't even get the samples in the posts working (let alone applying them to my code.)
How to save an array of objects to NSUserDefault with swift?
(Swift) Storing and retrieving Array to NSUserDefaults
How to save an array of custom structs to plist swift
Can you please let me know how to get this working?
Upvotes: 3
Views: 1750
Reputation: 93161
Your code is too verbose:
true
or false
is enough for the compiler to deduce that you want Bool
. Swift is built around the idea that the compiler can be made very smart to lighten the on the programmer.Now, on to the topic of archiving: you will be dismayed to learn that NSCoding
is only available to object which inherits from NSObject
. Structs can not be made to conform to NSCoding
. You need to wrap it around in a helper class.
Here's what I came up with:
protocol ArchivableStruct {
var dataDictionary: [String: AnyObject] { get }
init(dataDictionary aDict: [String: AnyObject])
}
class StructArchiver<T: ArchivableStruct>: NSObject, NSCoding {
var structValue: T
init(structValue: T) {
self.structValue = structValue
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(self.structValue.dataDictionary, forKey: "dataDictionary")
}
required init?(coder aDecoder: NSCoder) {
let dataDictionary = aDecoder.decodeObjectForKey("dataDictionary") as! [String: AnyObject]
self.structValue = T(dataDictionary: dataDictionary)
}
}
The dictionary is what gets archived. On the way back, we simply unpack this Dictionary to get the struct. We don't need to worry about QUERY_SETTINGS_KEY
and defaults
because the former is a constant and the later has its own data source (NSUserDefaults
).
struct QuerySettings: ArchivableStruct {
let QUERY_SETTINGS_KEY = "querysettings"
let defaults = NSUserDefaults.standardUserDefaults()
var includeCompletedReminders = false
var includeRemindersWithNoDueDate = true
init(includeCompletedReminders: Bool, includeRemindersWithNoDueDate: Bool) {
self.includeCompletedReminders = includeCompletedReminders
self.includeRemindersWithNoDueDate = includeRemindersWithNoDueDate
}
// --------------------------------------
// ArchivableStruct protocol
// --------------------------------------
var dataDictionary: [String: AnyObject] {
get {
return [
"includeCompletedReminders": self.includeCompletedReminders,
"includeRemindersWithNoDueDate": self.includeRemindersWithNoDueDate
]
}
}
init(dataDictionary aDict: [String : AnyObject]) {
self.includeCompletedReminders = aDict["includeCompletedReminders"] as! Bool
self.includeRemindersWithNoDueDate = aDict["includeRemindersWithNoDueDate"] as! Bool
}
}
struct
to the helper classSince you are not archiving a QuerySetting
struct but an array of them, there is an extra step to map it to StructArchiver<T>
so that your array can be archived:
// Set up a sample array
let a = QuerySettings(includeCompletedReminders: true, includeRemindersWithNoDueDate: false)
let b = QuerySettings(includeCompletedReminders: false, includeRemindersWithNoDueDate: true)
let sampleArray = [a, b]
// Archive
let wrapperArray = sampleArray.map { StructArchiver(structValue: $0) }
let data = NSKeyedArchiver.archivedDataWithRootObject(wrapperArray)
// Unarchive
let unwrapperArray = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! [StructArchiver<QuerySettings>]
let unarchivedArray = unwrapperArray.map { $0.structValue }
Upvotes: 4