1

I need to store a dictionary that which can contain a nil as a value

Example

var someOptionalVar: String? = nil

var dict: [String: AnyObject?] = [
    "someOptionalVar": self.someOptionalVar
]

defaults.setObject(dict, forKey: self.nsUserDefaultsKey)

But it gives me this error

Cannot convert value of type '[String: AnyObject?]' to expected argument type 'AnyObject?'

I know I could leave the nil variables and then when I'm parsing the dictionary from NSUserDefaults I would set variables (corresponding to the missing properties) to nil, but this is not what I would like to do.

So how can I store nil values in NSUserDefaults ?

nhgrif
  • 61,578
  • 25
  • 134
  • 173
Stevik
  • 1,092
  • 2
  • 16
  • 37
  • This looks similar : http://stackoverflow.com/a/34053503/3402095 – Whirlwind Apr 10 '16 at 15:32
  • I was just about to post that link. It looks like it won't work with optionals because they can't be represented by Foundation types. – WERUreo Apr 10 '16 at 15:34
  • @Whirlwind Similar but not the same, that was an `Array` of `Optional` and this is a `Dictionary` of `Optional`. If you want to explicitly note in the defaults that a value is `nil` for a key then `NSNull` is probably best. The key can also be omitted and value assumed to be `nil` if the key-value pair is not in the `Dictionary`. –  Apr 10 '16 at 15:35
  • @KennethBruno Yup. I said similar :)That is closest I've found using search. As well as this: http://stackoverflow.com/a/25753088/3402095. Still, not the answer to this question, but definitely worth of reading. Personally, I've never tried to store dictionary with optionals inside of an `NSUserDefaults` ... Interesting question... – Whirlwind Apr 10 '16 at 15:46
  • Not putting the nil properties in the dictionary or putting them in as nil achieves the same result. When you try to retrieve the value for that key, you get nil either way. – nhgrif Apr 10 '16 at 15:49
  • 2
    And at the end of the day, you can't store anything in NSUserDefaults that couldn't be bridged to one of the Objective-C types... A Swift dictionary has to be bridge-able to NSDictionary, and that means no optionals. – nhgrif Apr 10 '16 at 15:51
  • @nhgrif It's not going to be the same, though. The subscript operator will return `AnyObject??`. So `if let x = dict["someOptionalVar"] {` will succeed for `nil` values (and `x` will be `AnyObject?`). This is a really dangerous thing to build with lots of sharp edges and I expect to inject confusing bugs here and there, but it's definitely different than `[String: AnyObject]`. – Rob Napier Apr 10 '16 at 15:54
  • 1
    In a Swift `Dictionary` both keys and values must be non-optional because assigning `nil` to a key removes the key from the dictionary. – vadian Apr 10 '16 at 15:54
  • @RobNapier you are correct in that is technically different, but I'd bet anything that for the OP's use case, the difference doesn't matter and he'd be perfectly fine flattening his `[String: AnyObject?]` into a `[String: AnyObject]`. – nhgrif Apr 10 '16 at 15:56
  • @vadian You *could* do this: `dict[key] = Optional.None` – nhgrif Apr 10 '16 at 15:57
  • @nhgrif That won't work the way you think it will. You'd need to use `dict[key] = Optional.None` to get the expected (?!?!?) behavior. This is a great example of the danger of this type. As you've written it, `Optional.None` will be interpreted as `Optional>.None` and delete the value. – Rob Napier Apr 10 '16 at 15:59
  • @Stevik Can you please clarify what actually you are trying to do? BTW you can not set [String: AnyObject?] in the user-defaults. – Shoaib Apr 10 '16 at 16:06

1 Answers1

6

Use NSNull() instead of nil, and declare the dictionary to contain only non-optionals:

var someOptionalVar: String? = nil

var dict: [String: AnyObject] = [
    "someOptionalVar": self.someOptionalVar ?? NSNull()
]

defaults.setObject(dict, forKey: self.nsUserDefaultsKey)
Lasse
  • 490
  • 3
  • 8
  • 4
    You can replace `(self.someOptionalVar != nil) ? self.someOptionalVar : NSNull()` with the [nil coalescing operator](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-ID72) `??` like this: `self.someOptionalVar ?? NSNull()` –  Apr 10 '16 at 15:25
  • 1
    Your answer isn't quite the same as the question. The OP's code creates a dictionary of [String: AnyObject?] but you have a dictionary of [String: AnyObject]. I don't know the answer to this myself, but it seems to have to do with the optional AnyObject in the dictionary. I rewrote the OP's code but left the optional off AnyObject and it worked fine. – WERUreo Apr 10 '16 at 15:30
  • 1
    @WERUreo An optional in Swift translates to a nil pointer in Foundation classes. Foundation collection classes can't contain nil so [`NSNull` is used instead](http://www.cocoawithlove.com/2008/06/doing-things-in-cocoa-with.html). Thus anything you want to store in the defaults should be converted from `Optional` to a concrete value or `NSNull`. Alternatively you can convert the `Dictionary` to use non-`Optional` values and remove the keys that have `nil` values. –  Apr 10 '16 at 15:43
  • My mistake. Would it help to mention in the answer that not only is NSNull() being used but the dictionary being added to NSUserDefaults is not defined to contain an optional? – WERUreo Apr 10 '16 at 15:53
  • 1
    Thanks @WERUreo for your comments. I edited the answer to include your suggestions – Lasse Apr 10 '16 at 16:03
  • Same goes for you @KennethBruno. Thanks – Lasse Apr 10 '16 at 16:03