Checking if an object is a given type in Swift
If you want to check against a specific type you can do the following:
if let stringArray = obj as? [String] {
// obj is a string array. Do something with stringArray
}
else {
// obj is not a string array
}
You can use "as!" and that will throw a runtime error if obj
is not of type [String]
let stringArray = obj as! [String]
You can also check one element at a time:
let items : [Any] = ["Hello", "World"]
for obj in items {
if let str = obj as? String {
// obj is a String. Do something with str
}
else {
// obj is not a String
}
}
Swift - Check if object is of a given type (where the type has been passed as function argument)
I don't know where you are taking myArray
. You probably need to create an extension to Array for MyBaseClass. Like this:
extension Array where Element: MyBaseClass {
func myFilter<T: MyBaseClass>(objectType: T.Type) -> [T] {
var filteredArray: [T] = []
for object in self {
if let object = object as? T {
filteredArray.append(object)
}
}
return filteredArray
}
}
Then you can call:
// Example call
let array = [SubclassA(), SubclassA(), SubclassC(), SubclassD()]
array.myFilter(objectType: SubclassD.self) // == [SubclassD()]
EDIT:
Easy solution if you want a return type of myFilter
to be just MyBaseClass
and you don't want to change the original array would be this:
array.filter { $0 is SubclassD }
Checking if object is not of type in swift
Using swift syntax this is one way to do this
let animals: [Animals] = [Horse(), Cat(), Dog(), Spider()]
var chosenAnimals = animals.filter { type(of: $0) != Spider.self }
alternatively
var chosenAnimals = animals.filter { !($0 is Spider) }
Check whether Swift object is an instance of a given metatype
Unfortunately, you can currently only use a named type with the is
operator, you cannot yet use an arbitrary metatype value with it (although really IMO you ought to be able to).
Assuming you have control over the creation of the metatypes that you want to compare against, one solution that achieves the same result would be to create a wrapper type with an initialiser that stores a closure that performs the is
check against a generic placeholder:
struct AnyType {
let base: Any.Type
private let _canCast: (Any) -> Bool
/// Creates a new AnyType wrapper from a given metatype.
/// The passed metatype's value **must** match its static value,
/// i.e `T.self == base`.
init<T>(_ base: T.Type) {
precondition(T.self == base, """
The static value \(T.self) and dynamic value \(base) of the passed \
metatype do not match
""")
self.base = T.self
self._canCast = { $0 is T }
}
func canCast<T>(_ x: T) -> Bool {
return _canCast(x)
}
}
protocol P {}
class C : P {}
class D : C {}
let types = [
AnyType(P.self), AnyType(C.self), AnyType(D.self), AnyType(String.self)
]
for type in types {
print("C instance can be typed as \(type.base): \(type.canCast(C()))")
print("D instance can be typed as \(type.base): \(type.canCast(D()))")
}
// C instance can be typed as P: true
// D instance can be typed as P: true
// C instance can be typed as C: true
// D instance can be typed as C: true
// C instance can be typed as D: false
// D instance can be typed as D: true
// C instance can be typed as String: false
// D instance can be typed as String: false
The only limitation of this approach is that given we're performing the is
check with T.self
, we have to enforce that T.self == base
. For example, we cannot accept AnyType(D.self as C.Type)
, as then T.self
would be C.self
while base
would be D.self
.
However this shouldn't be a problem in your case, as we're just constructing AnyType
from metatypes that are known at compile time.
If however you don't have control over the creation of the metatypes (i.e you get handed them from an API), then you're quite a bit more limited with what you can do with them.
As @adev says, you can use type(of:)
in order to get the dynamic metatype of a given instance, and the ==
operator to determine if two metatypes are equivalent. However, one problem with this approach is that it disregards both class hierarchies and protocols, as a subtype metatypes will not compare equal with a supertype metatypes.
One solution in the case of classes is to use Mirror
, as also shown in this Q&A:
/// Returns `true` iff the given value can be typed as the given
/// **concrete** metatype value, `false` otherwise.
func canCast(_ x: Any, toConcreteType destType: Any.Type) -> Bool {
return sequence(
first: Mirror(reflecting: x), next: { $0.superclassMirror }
)
.contains { $0.subjectType == destType }
}
class C {}
class D : C {}
print(canCast(D(), toConcreteType: C.self)) // true
print(canCast(C(), toConcreteType: C.self)) // true
print(canCast(C(), toConcreteType: D.self)) // false
print(canCast(7, toConcreteType: Int.self)) // true
print(canCast(7, toConcreteType: String.self)) // false
We're using sequence(first:next:)
to create a sequence of metatypes from the dynamic type of x
through any superclass metatypes it might have.
However this method still won't work with protocols. Hopefully a future version of the language will provide much richer reflection APIs that allow you to compare the relationship between two metatype values.
However, given the above knowledge of being able to use Mirror
, we can use it to lift the aforementioned restriction of T.self == base
from our AnyType
wrapper on by handling class metatypes separately:
struct AnyType {
let base: Any.Type
private let _canCast: (Any) -> Bool
/// Creates a new AnyType wrapper from a given metatype.
init<T>(_ base: T.Type) {
self.base = base
// handle class metatypes separately in order to allow T.self != base.
if base is AnyClass {
self._canCast = { x in
sequence(
first: Mirror(reflecting: x), next: { $0.superclassMirror }
)
.contains { $0.subjectType == base }
}
} else {
// sanity check – this should never be triggered,
// as we handle the case where base is a class metatype.
precondition(T.self == base, """
The static value \(T.self) and dynamic value \(base) of the passed \
metatype do not match
""")
self._canCast = { $0 is T }
}
}
func canCast<T>(_ x: T) -> Bool {
return _canCast(x)
}
}
print(AnyType(D.self as C.Type).canCast(D())) // true
The case where T.self
is a class metatype should be the only case where T.self != base
, as with protocols, when T
is some protocol P
, T.Type
is P.Protocol
, which is the type of the protocol itself. And currently, this type can only hold the value P.self
.
How do you find out the type of an object (in Swift)?
Swift 3 version:
type(of: yourObject)
Related Topics
Call a Method from a String in Swift
Extend Array Types Using Where Clause in Swift
Print Without Newline in Swift
Swift - Extra Argument in Call
How to Use Userdefaults With Swiftui
Trapping Signals in a Swift Command Line Application
How to Get the Current Date in Short Format in Swift
Swiftui Picker Separate Texts For Selected Item and Selection View
Dyld: Library Not Loaded: @Rpath/Libswift_Stdlib_Core.Dylib
How to Convert a View (Not Uiview) to an Image
Transparent Background For Modally Presented Viewcontroller
How to Make an Enum Decodable in Swift
Why Is 'Nil' Not Compatible With 'Unsafepointer≪Cgaffinetransform≫' in Swift 3
Load Local Web Files & Resources in Wkwebview
Any Reason Not Use Use a Singleton "Variable" in Swift