3

Starting with swift3 I'm having trouble reliably detecting nil/NSNull. In this snippet here, instead of nil, I'm getting an Optional(_SwiftValue) value that I don't know how to detect.

I have tried checking classForCoder without luck.

Elsewhere there is talk about id-as-Any causing this kind of problem

func testVotingMachine() {
    let vote: String? = nil // Can be a string or nil
    //let vote: String? = "president" // Can be a string or nil
    var dict = [String: AnyObject?]()
    dict["vote"] = vote as AnyObject?
    let obj: AnyObject? = dict as AnyObject?

    guard let dict2 = obj as? Dictionary<String, AnyObject?> else {
        fatalError()
    }

    let value = dict2["vote"] as AnyObject?
    let didVote = notNil(objectOrNil: value)
    XCTAssertFalse(didVote)   // BOOM! notNil() fails to detect nil
}

func notNil(objectOrNil: AnyObject?) -> Bool {
    guard let object: AnyObject = objectOrNil else {
        return false
    }
    if object is NSNull {
        return false
    }
    //if object.classForCoder == NSNull.classForCoder() {
    //  return false
    //}
    // TODO: how to check for? Optional(_SwiftValue)
    print("class for coder: \(object.classForCoder)")
    return true
}

I need to sanitise my data before passing it to JSONSerialization.data(). Thus I need to detect Optional(_SwiftValue)

Community
  • 1
  • 1
neoneye
  • 50,398
  • 25
  • 166
  • 151
  • Looks like you get the expected result if you make one tiny change to `dict`: `var dict = [String : AnyObject]`. – rmaddy Oct 24 '16 at 21:55
  • This is the smallest snippet I could make that inserts an `Optional(_SwiftValue)` into a dictionary. It's ugly. Is it an internal type that is not supposed to be exposed to developers. Or is it something swift developers have to deal with. Any ideas how to detect the `Optional(_SwiftValue)`. – neoneye Oct 24 '16 at 22:30

1 Answers1

2

An String? is shorthand for Optional<String>. When it's nil, it's actually represented as Optional<String>.none, which qualifies is as a non-nil AnyObject so your test didn't work.

You can fix it like this:

func notNil(objectOrNil: AnyObject?) -> Bool {
    guard let object: AnyObject = objectOrNil,
        object as! _OptionalNilComparisonType != AnyObject?.none else {
        return false
    }
    if object is NSNull {
        return false
    }
    //if object.classForCoder == NSNull.classForCoder() {
    //  return false
    //}
    // TODO: how to check for? Optional(_SwiftValue)
    print("class for coder: \(object.classForCoder)")
    return true
}
Code Different
  • 90,614
  • 16
  • 144
  • 163
  • The problem is not solved yet. If I pass in a String then it thinks that it's nil. Where it should think that it's not nil. So I'm unflagging this as being the answer. – neoneye Oct 27 '16 at 10:46