final public class SingleValue<Value> {
public var value: Value
public init(value: Value) {
self.value = value
}
}
public class ValueWrapper<Value> {
public var value: Value
public init(value: Value) {
self.value = value
}
}
let single = SingleValue<String>.init(value: "string")
let wrapper = ValueWrapper<Any>.init(value: single)
if wrapper.value is SingleValue<Any> {
print("is SingleValue<Any>") // not invoked
}
if wrapper.value is SingleValue<String> {
print("is SingleValue<String>") // print: is SingleValue<String>
}
Hi, All:
As described in the code above, I want to check if wrapper.value is SingleValue, but the test is failed. How can I do this properly?
Ah, SingleValue is a generic type (which generate types) and without a given type parameter it is not a concrete type. So you can't do that. SingleValue<Any> is also a concrete type, just like (but a different one than) SingleValue<String>.
You can have all SingleValue types conform to a protocol or inherit from a base class (whatever makes most sense for your use case) and check against that instead, eg:
protocol SingleValueProtocol {}
public class SingleValueBaseClass { }
final public class SingleValue<Value>: SingleValueBaseClass, SingleValueProtocol {
public var value: Value
public init(value: Value) {
self.value = value
}
}
public class ValueWrapper<Value> {
public var value: Value
public init(value: Value) {
self.value = value
}
}
let single = SingleValue<String>.init(value: "string")
let wrapper = ValueWrapper<Any>.init(value: single)
if wrapper.value is SingleValue<Any> { print("is SingleValue<Any>") } // not invoked
if wrapper.value is SingleValue<String> { print("is SingleValue<String>") } // invoked
if wrapper.value is SingleValueProtocol { print("conforms to SingleValueProtocol") } // invoked
if wrapper.value is SingleValueBaseClass { print("is SingleValueBaseClass") } // invoked
public protocol SingleValueProtocol {}
final public class SingleValue<Value> : SingleValueProtocol {
public var value: Value
public init(value: Value) {
self.value = value
}
}
public class ValueWrapper<Value> {
public var value: Value
public init(value: Value) {
self.value = value
}
}
public class SingleView {
public var value: SingleValue<Any>
public init(value: SingleValue<Any>) {
self.value = value
}
}
let single = SingleValue<String>.init(value: "string")
let wrapper = ValueWrapper<Any>.init(value: single)
if wrapper.value is SingleValueProtocol {
let view = SingleView.init(value: wrapper.value as! SingleValue<Any>) // Error: Could not cast value of type 'SingleValue<Swift.String>' to 'SingleValue<Any>'
}
As described in the code above, I'm drilled into a trap, my SingleView doesn't want to introduce some associated type. So, even I knew wrapper.value is SingleValue<_>, but I still can not use it to init my SingleView.
Finally, I found the better solution, the code as follows:
final public class SingleValue<Value> {
public var value: Value
public init(value: Value) {
self.value = value
}
}
public class ValueWrapper<Value> {
public var value: Value
public init(value: Value) {
self.value = value
}
}
public class SingleView {
public var value: SingleValue<Any>
public init(value: SingleValue<Any>) {
self.value = value
}
}
let single = SingleValue<Any>.init(value: "string" as Any)
let wrapper = ValueWrapper<Any>.init(value: single)
if let v = wrapper.value as? SingleValue<Any> {
let view = SingleView.init(value: v) //SingleView
}
But note that your last example only works because you defined single to be a SingleValue<Any> instead of a SingleValue<String> (as in your original example). Changing it back will make this solution it fail too:
let single = SingleValue<String>.init(value: "string") // <-- Changed to be as in original example
let wrapper = ValueWrapper<Any>.init(value: single)
if let v = wrapper.value as? SingleValue<Any> {
let view = SingleView.init(value: v) //SingleView
}
Conversely, your original example would have worked (but you'd have the same drilling down problem because of is vs as?) if single had been declared as in your last example:
final public class SingleValue<Value> {
public var value: Value
public init(value: Value) {
self.value = value
}
}
public class ValueWrapper<Value> {
public var value: Value
public init(value: Value) {
self.value = value
}
}
//let single = SingleValue<String>.init(value: "string")
let single = SingleValue<Any>.init(value: "string" as Any)
let wrapper = ValueWrapper<Any>.init(value: single)
if wrapper.value is SingleValue<Any> {
print("is SingleValue<Any>") // invoked
}
if wrapper.value is SingleValue<String> {
print("is SingleValue<String>") // not invoked
}
I agree that there are some special cases which might lead your intuition that way, but look at the following example program and think about …
… why your intuition is true for Array and Optional but not for eg CollectionOfOne (or any generic type we define ourselves).
… the reason for the error on the last line.
func check<T>(if value: Any, isType _: T.Type) {
if value is T {
print("Value of type \(type(of: value)) is \(T.self)")
} else {
print("Value of type \(type(of: value)) is not \(T.self)")
}
}
let arrInt = Array<Int>()
let cooInt = CollectionOfOne<Int>(123)
let optInt = Optional<Int>.none
let rngInt = Range<Int>(uncheckedBounds: (lower: 0, upper: 1))
check(if: arrInt, isType: Array<Any>.self)
check(if: cooInt, isType: CollectionOfOne<Any>.self)
check(if: optInt, isType: Optional<Any>.self) // WARNING: Expression implicitly coerced from 'Optional<Int>' to 'Any'
//check(if: rngInt, isType: Range<Any>.self) // ERROR: Type 'Any' does not conform to protocol 'Comparable'
which will print:
Value of type Array<Int> is Array<Any>
Value of type CollectionOfOne<Int> is not CollectionOfOne<Any>
Value of type Optional<Int> is Optional<Any>