I've got the following code, which illustrates a problem I haven't figured out how to solve cleanly, that is:
How can I make a function (isNil) that will return true for both nil, and Optional(nil), but false for anything else?
class Foo {
var baz : Date? = nil
subscript(key: String) -> Any? {
get {
let m = Mirror(reflecting: self)
for child in m.children {
if (child.label == key) { return child.value }
}
return nil
}
}
}
// this works unless the field is an Optional(nil)
func isNil(_ field: Any?) -> Bool {
if field == nil { return true }
return false
}
// this sort of works in a really terrible hacked way
func isNilViaString(_ field: Any?) -> Bool {
if field == nil { return true }
return "\(field.debugDescription)" == "Optional(nil)"
}
// this returns true as expected
print("isNil(nil) = \(isNil(nil))")
var optionalNil = Foo()["baz"]
// I'd like this to return true as well
print("isNil(optionalNil) = \(isNil(optionalNil))")
// this returns true, but is super hacky
print("isNilViaString(optionalNil) = \(isNilViaString(optionalNil))")
// this is an example of a problem with the isNilViaString method
print("isNilViaString(optionalNil) = \(isNilViaString("Optional(nil)"))")
CodePudding user response:
isNil
is best based on flattening the optionality of the wrapped value. (You may not actually have a use for isNil
if you incorporate this directly into your subscript.)
If you don't care about the unwrapping failure details:
public extension Any? {
/// Represent an `Optional` with `Any?` instead of `Any`.
///
/// If `any` is an optional, this instance will copy it.
/// Otherwise, this instance will wrap it.
///
/// - Note: Use this to avoid an `Any?` actually representing an `Any??`.
init(flattening any: Any) {
switch any {
case let optional as Self:
self = optional
}
}
var isNil: Bool { flatMap(Self.init) == nil }
}
subscript(key: String) -> Any? {
( Mirror(reflecting: self).children
.first { $0.label == key }?
.value
).flatMap(_?.init)
}
If you do:
public extension Optional {
/// Represents that an `Optional` was `nil`.
struct UnwrapError: Error & Equatable {
public init() { }
}
}
public extension Any? {
/// The wrapped value, whether `Wrapped` is an `Optional` or not.
/// - Throws: `Any?.UnwrapError` when `nil`,
/// or `Any??.UnwrapError` when wrapping another `Optional` that is `nil`.
var doublyUnwrapped: Wrapped {
get throws {
switch self {
case let doubleWrapped?? as Self?:
return doubleWrapped
case _?:
throw Self?.UnwrapError()
case nil:
throw UnwrapError()
}
}
}
var isNil: Bool { (try? doublyUnwrapped) == nil }
}
subscript(key: String) -> Any {
get throws {
try (
Mirror(reflecting: self).children
.first { $0.label == key }?
.value
)
.doublyUnwrapped
}
}
CodePudding user response:
Once you check if field
is nil or not, you can then use a switch
with a case that checks if the value is an optional.
Here is an updated isNil
method:
func isNil(_ field: Any?) -> Bool {
if let field {
switch field {
case let optional as Optional<Any>:
if case .some = optional {
// This was an Optional but not nil
return false
} else {
// This was an Optional<nil>
return true
}
default:
// This was not an optional
return false
}
} else {
// nil was passed in
return true
}
}
Some updated test cases:
print("isNil(nil) = \(isNil(nil))")
var optionalNil = Foo()["baz"]
print("isNil(optionalNil) = \(isNil(optionalNil))")
var foo = Foo()
foo.baz = Date()
var optional = foo["baz"]
print("isNil(optional) = \(isNil(optional))")
print("isNil(Date()) = \(isNil(Date()))")
Output:
isNil(nil) = true
isNil(optionalNil) = true
isNil(optional) = false
isNil(Date()) = false
CodePudding user response:
This can be checked using Optional.none
func isNil(_ value: Any?) -> Bool {
if case Optional.none = value { return true }
return false
}
CodePudding user response:
You can convert them to AnyObject
then compare with NSNull
:
func isNil(_ field: Any?) -> Bool {
return field as AnyObject is NSNull
}
CodePudding user response:
This might help you. But this will work only for nil and Optional(nil). If optional will have any value then it will not work.
func isOptional(_ instance: Any?) -> Bool {
if instance == nil { return true }
if let ins = instance {
let mirror = Mirror(reflecting: ins)
let style = mirror.displayStyle
return style == .optional
}
return false
}
And the results will be:
let a: Int = 1 // "1\n"
let b: Int? = 2 "Optional(2)\n"
let c: Double = 3.0 // "3.0\n"
let d: Double? = 4.0 // "Optional(4.0)\n"
let e: NSString = "Hello" // "Hello\n"
let f: NSString? = "Hello" // "Optional(Hello)\n"
let optionalNil = Foo()["baz"] // "Optional(nil)\n"
isOptional(a) // fasle
isOptional(b) // true - warning
isOptional(c) // false
isOptional(d) // true - warning
isOptional(e) // false
isOptional(f) // false
isOptional(optionalNil) // true
isOptional(nil) // true
Originally answered by Kaz Yoshikawa. I just modified it as per your requirement.