neoneye
neoneye

Reputation: 52231

How to check for nil/NSNull/optional, when Optional(_SwiftValue) is involved

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)

Upvotes: 3

Views: 865

Answers (1)

Code Different
Code Different

Reputation: 93191

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
}

Upvotes: 2

Related Questions