Search code examples
swiftoption-typensubiquitouskeyvaluestoreswift-dictionary

How to Specify Dictionary Type in Key-Value Store with Swift?


Assume the following Dictionary:

var example: [String: (identifier: String, regex: NSRegularExpression)] = ["test": (identifier: "example", regex: try! NSRegularExpression(pattern: "test", options: []))]

And I want to store it as follows:

let keyStore = NSUbiquitousKeyValueStore.default()
keyStore.set(example, forKey: "ex")

My problem is that when I try to access it:

let test: [String: (identifier: String, regex: NSRegularExpression)] = keyStore.dictionary(forKey: "ex") as! [String: (identifier: String, regex: NSRegularExpression)]

I receive the following error:

Unwrapped optional value

Why is this?


Solution

  • You are trying to hand your dictionary across to Objective-C, which requires an Objective-C NSDictionary; but you cannot store a Swift tuple as a value in an Objective-C NSDictionary. Moreover, the rules for NSUbiquitousKeyValueStore are even more stringent: not only must this be an NSDictionary, you can only use property list types, which are extremely limited. You would need to do something like wrap a CGSize in an NSValue and archive that to an NSData in order to use it here:

        let sz = CGSize(width:10, height:20)
        let val = NSValue(cgSize:sz)
        let dat = NSKeyedArchiver.archivedData(withRootObject: val)
        let example = ["test": dat]
    
        let keyStore = NSUbiquitousKeyValueStore.default()
        keyStore.set(example, forKey: "ex")
    

    To get the value back out, reverse that procedure.

        if let dict = keyStore.dictionary(forKey: "ex") {
            if let ex = dict["test"] as? Data {
                if let v = NSKeyedUnarchiver.unarchiveObject(with: ex) as? NSValue {
                    print(v.cgSizeValue) // (10.0, 20.0)
                }
            }
        }