Swift NSUserDefaults not saving Dictionary?
Update for Swift 2, Xcode 7: As @atxe noticed, NSUserDefaults dictionaries are now mapped as [String, AnyObject]
. This is a
consequence of the Objective-C "lightweight generics" which allow
to declare the Objective-C method as
- (NSDictionary<NSString *,id> *)dictionaryForKey:(NSString *)defaultName
(Default objects must be property lists and in particular the dictionary
keys can only be strings.)
On the other hand, a Swift dictionary is bridged automatically if possible, so the original code from the question works (again):
let jo = [
"a" : "1.0",
"b" : "2.0"
]
let akey = "aKey"
// Swift 2:
userDefaults.setObject(jo, forKey: akey)
// Swift 3:
userDefaults.set(jo, forKey: akey)
Original answer for Swift 1.2:
The user defaults can store NSDictionary
objects. These are mapped to Swift
as [NSObject : AnyObject]
:
var jo : [NSObject : AnyObject] = [
"a" : "1.0",
"b" : "2.0"
]
userDefaults.setObject(jo, forKey: akey)
var isOk = userDefaults.synchronize()
And note that dictionaryForKey()
returns an optional, so you should check it
for example with an optional assignment:
if let data0 = userDefaults.dictionaryForKey(akey) {
print(data0)
} else {
print("not set")
}
// Output: [b: 2.0, a: 1.0]
Save and Load Custom Dictionary - NSUserDefaults
Swift 4
Among basic types, UserDefaults
can save any object that conforms to Codable
protocol. Dictionary
is one of the types that implements this protocol. You don't even need to write any custom code:
let dictionary = ["name": "Adam"]
// Save to User Defaults
UserDefaults.standard.set(dictionary, forKey: "names")
// Read from User Defaults
let saved = UserDefaults.standard.value(forKey: "names") as? [String: String]
See more info about Codable
Swift 3
You can use UserDefaults
to save a Dictionary
as long as key and value types are types that can be represented in a plist format (NSNumber
, Data
, etc.). If that's not the case, we can always serialise other types to Data
when writing and deserialise from Data
when reading. It can be accomplished with pretty simple extension of UserDefaults
using NSKeyArchiver
:
extension UserDefaults {
/// Save dictionary on key
open func set<Key, Value>(dictionary: [Key: Value]?, forKey key: String) {
let data = NSKeyedArchiver.archivedData(withRootObject: dictionary as Any)
set(data, forKey: key)
}
// Retrieve dictionary for key
open func dictionary<Key, Value>(forKey key: String) -> [Key: Value]? {
guard let data = object(forKey: key) as? Data else { return nil }
return NSKeyedUnarchiver.unarchiveObject(with: data) as? [Key: Value]
}
}
Now you can call these methods:
let ages = ["Adam": 25]
// Save
UserDefaults.standard.set(dictionary: ages, forKey: "ages")
// Read
let saved: [String: Int]? = UserDefaults.standard.dictionary(forKey: "ages")
print(saved) // Optional(["Adam": 25])
Swift 2
Save custom data
func setCustomDictionary(dict: [Int: Bool]) {
let keyedArch = NSKeyedArchiver.archivedDataWithRootObject(dict)
NSUserDefaults.standardUserDefaults().setObject(keyedArch, forKey: "dictionary")
}
Retrieve data
func getDictionary() -> [Int: Bool]? {
let data = NSUserDefaults.standardUserDefaults().objectForKey("dict")
let object = NSKeyedUnarchiver.unarchiveObjectWithData(data as! NSData)
return object as? [Int: Bool]
}
Usage
var customDictionary = [Int: Bool]()
customDictionary[2] = true
customDictionary[3] = false
// Store data in NSUserDefaults
setCustomDictionary(customDictionary)
// Get data from NSUserDefaults
let userDefaultsDictionary = getDictionary()
How to Store multiple dictionary in NSUserDefaults
Every time you are using the same key and replacing the existing dictionary object...
// Using the same key will overwrite the last saved dictionary.
[[NSUserDefaults standardUserDefaults] setObject:discRege forKey:@"ABC"];
Instead of storing it as a dictionary, store it as an array of dictionaries. Whenever you add new registration, fetch the saved array, add new dictionary object into it and update the userDefaults with that array.
mutableArray = [[[NSUserDefaults standardUserDefaults]objectForKey:@"ABC"] mutableCopy];
[mutableArray addObject:discReg];
[[NSUserDefaults standardUserDefaults] setObject:mutableArraay forKey:@"ABC"];
[[NSUserDefaults standardUserDefaults] synchronize];
Hope it helps.
Save Dictionary into NSUserDefaults Swift
I managed to save my custom Dictionary using Realm!
Instead of a struct I use a different class like this:
import RealmSwift
class Sensors : Object {
dynamic var sensorName = ""
dynamic var sensorType = ""
dynamic var sensorSub = ""
}
and then I fill it like this:
var useOfRealm = try! Realm()
var allSensors = useOfRealm.objects(Sensors.self)
var saving = Sensors()
func fillThis() {
try! useOfRealm.write {
saving.sensorName = "something"
saving.sensorType = "something"
saving.sensorSub = "something"
useOfRealm.add(saving)
}
}
Use the function with parameters so you can fill the 'Dictionary' Dynamically.
Use allSensors
so you can retrieve everything that you want.
Save dictionary in userdefaults in swift 3 with xcode 8
This problem seems to be caused by having two versions of xcode/simulator installed.
What worked for me was uninstalling xcode 7 and just keeping xcode 8 beta on my system. Emptying trash, resetting the simulator and running. I also restarted my computer.
After following these steps the simulator is able to save to UserDefaults.
Saving dictionary into NSUserDefaults
From the NSUserDefaults documentation:
The value parameter can be only property list objects: NSData,
NSString, NSNumber, NSDate, NSArray, or NSDictionary. For NSArray and
NSDictionary objects, their contents must be property list objects.
See “What is a Property List?” in Property List Programming Guide.
You cannot store a dictionary with elements of Element
in the dictionary.
One option is to make sure Element
implements the NSCoding protocol and then use NSKeyedArchiver to convert your dictionary into NSData:
// Storing the dictionary
var data = NSKeyedArchiver.archivedDataWithRootObject(theDict)
NSUserDefaults.standardUserDefaults().setObject(data, forKey: tableViewData)
// Retrieving the dictionary
var outData = NSUserDefaults.standardUserDefaults().dataForKey(tableViewData)
var dict = NSKeyedUnarchiver.unarchiveObjectWithData(outData)
Your protocol implementation would look like something like this:
init(coder aDecoder: NSCoder!) {
self.state = State.fromRaw(aDecoder.decodeObjectForKey("state") as String)
}
func encodeWithCoder(aCoder: NSCoder!) {
aCoder.encodeObject(self.state.toRaw(), forKey: "state")
}
You cannot store an Enum, but you can store the raw representation of it which is a string.
Save dictionary to UserDefaults
Dictionaries are Codable objects by default, you can use the following extensions to save them to UserDefaults
extension UserDefaults {
func object<T: Codable>(_ type: T.Type, with key: String, usingDecoder decoder: JSONDecoder = JSONDecoder()) -> T? {
guard let data = self.value(forKey: key) as? Data else { return nil }
return try? decoder.decode(type.self, from: data)
}
func set<T: Codable>(object: T, forKey key: String, usingEncoder encoder: JSONEncoder = JSONEncoder()) {
let data = try? encoder.encode(object)
self.set(data, forKey: key)
}
}
They can be used like this:
let test = [1:"me"]
UserDefaults.standard.set(object: test, forKey: "test")
let testFromDefaults = UserDefaults.standard.object([Int: String].self, with: "test")
This extension and many others are part of SwifterSwift, you might want to use it for your next iOS project :)
How do I save NSUserDefaults when dictionary key has a Null value
formValues
returns NSNull
for empty values which can't be saved in NSDictionary
as noted by Paulw11.
You can use this function to filter it.
let filtered = message.filter({!$1.isKindOfClass(NSNull)}).reduce([:]) { ( dic , e) -> [String:AnyObject] in
var dic = dic
dic[e.0 as! String] = e.1
return dic
}
and then save the filtered
in userDefaults
Related Topics
Table View Cellforrowatindexpath Warning
Update Nstouchbar on the Fly to Add/Remove Items Programmatically
Correct Way to Use Nwconnection for Long-Running Tcp Socket
How to Combine Two Nsdictionary in Swift
Arkit - Collision with Real World Objects
Tvos Textfield Transparent Background
Crash When Running on Device After Second Launch
Adding Elements on Many-To-Many Relation
How to Add a Left Bar Button Without Overriding the Natural Back Button
What's Wrong with My #If Target_Os_Simulator Code for Realm Path Definition
Swift Alamofire Add Custom Header to All Requests
Count the Number of Lines in a Swift String
Calculate the Number of Dimensions of a Multi-Dimensional Array in Swift
How to Masking the Last Number in Swift