10

I'm trying to store a dictionary in UserDefaults and always get app crash when the code runs. Here is the sample code which crashes the app when it is executed. I tried to cast it as NSDictionary or make it NSDictionary initially - got the same result.

class CourseVC: UIViewController {

let test = [1:"me"]

override func viewDidLoad() {
    super.viewDidLoad()

    defaults.set(test, forKey: "dict1")

    }

}
Shivam Tripathi
  • 1,405
  • 3
  • 19
  • 37
Roman
  • 154
  • 1
  • 1
  • 12
  • Maybe the Int inkey is the problem? Try 'let test = [1:"me"]'. work? –  Apr 15 '18 at 16:08
  • 1
    @Roman can you share your crash log... it’s useful to debug your application. Otherwise we can’t pinpoint exactly what’s wrong, as some of the source code seems to be missing (like where is the defaults variable declaration) and because your source code shows a Dictionary, but an answer is showing an NSDictionary. If you wanted to use the latter, consider making the first key a String. – Mihir Thanekar Apr 15 '18 at 16:33
  • Only property list dictionaries can be stored directly in user defaults. A property list dictionary must have string keys. – matt Apr 15 '18 at 23:56

2 Answers2

33

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 :)

Omar Albeik
  • 1,332
  • 13
  • 17
1

To store a NSDictionary (with non-string key) in NSUserDefaults you need to convert them to NSData first. Try this

let test = [1:"me"]
override func viewDidLoad() {
    super.viewDidLoad()
    let data = NSKeyedArchiver.archivedData(withRootObject: test)
    let defaults = UserDefaults.standard
    defaults.set(data, forKey: "dict1")
    if let data2 = defaults.object(forKey: "dict1") as? NSData {
        let dict = NSKeyedUnarchiver.unarchiveObject(with: data2 as Data)
        print(dict)
    }
}
Lenin
  • 675
  • 6
  • 17
  • 1
    I dont think you need archive the dict. Check the doc -> https://developer.apple.com/documentation/foundation/userdefaults – dahiya_boy Apr 15 '18 at 16:03
  • I save a dictionay to UserDefauls withat casting to data. –  Apr 15 '18 at 16:09
  • 1
    NSDictionary with non-string key can’t store to Userdefaults directly – Lenin Apr 15 '18 at 16:12
  • @Lenin Go through the doc, It is clearly mentioned that UserDefault supports Dict. So recheck the code in your xcode. – dahiya_boy Apr 15 '18 at 16:14
  • 2
    In this doc https://developer.apple.com/documentation/foundation/nsdictionary it is clearly mentioned that NSDictionaries key must be string. – Lenin Apr 15 '18 at 16:17
  • @Lenin thank you for answer. It works. But there is one problem. The code I provided in my question was sample. My real dictionary, which I need to store is [Int:Bool]. So after unarchiving it I get all my “trues” and “falses” replaced with ones and zeros. In the other words, my dictionary was [1:true, 2:true, 3:false...] and now it is [1 = 1, 2 = 1, 3 = 0...] So, it is no more a dictionary. – Roman Apr 15 '18 at 18:24
  • It's still a dictionary, it's just that the NSNumbers that were stored in the dictionary have been retrieved as int, not bool since user defaults doesn't know they were bools. Honestly, if you are trying to store complex data structures in UserDefaults you should probably be looking at some other storage mechanism – Paulw11 Apr 15 '18 at 20:47