I have an array that is made up of AnyObject
. I want to iterate over it, and find all elements that are array instances.
How can I check if an object is of a given type in Swift?
I have an array that is made up of AnyObject
. I want to iterate over it, and find all elements that are array instances.
How can I check if an object is of 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
}
}
In Swift 2.2 - 5 you can now do:
if object is String
{
}
Then to filter your array:
let filteredArray = originalArray.filter({ $0 is Array })
If you have multiple types to check:
switch object
{
case is String:
...
case is OtherClass:
...
default:
...
}
If you only want to know if an object is a subtype of a given type then there is a simpler approach:
class Shape {}
class Circle : Shape {}
class Rectangle : Shape {}
func area (shape: Shape) -> Double {
if shape is Circle { ... }
else if shape is Rectangle { ... }
}
“Use the type check operator (is) to check whether an instance is of a certain subclass type. The type check operator returns true if the instance is of that subclass type and false if it is not.” Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
In the above the phrase 'of a certain subclass type' is important. The use of is Circle
and is Rectangle
is accepted by the compiler because that value shape
is declared as Shape
(a superclass of Circle
and Rectangle
).
If you are using primitive types, the superclass would be Any
. Here is an example:
21> func test (obj:Any) -> String {
22. if obj is Int { return "Int" }
23. else if obj is String { return "String" }
24. else { return "Any" }
25. }
...
30> test (1)
$R16: String = "Int"
31> test ("abc")
$R17: String = "String"
32> test (nil)
$R18: String = "Any"
I have 2 ways of doing it:
if let thisShape = aShape as? Square
Or:
aShape.isKindOfClass(Square)
Here is a detailed example:
class Shape { }
class Square: Shape { }
class Circle: Shape { }
var aShape = Shape()
aShape = Square()
if let thisShape = aShape as? Square {
println("Its a square")
} else {
println("Its not a square")
}
if aShape.isKindOfClass(Square) {
println("Its a square")
} else {
println("Its not a square")
}
Edit: 3 now:
let myShape = Shape()
if myShape is Shape {
print("yes it is")
}
Assume drawTriangle is an instance of UIView.To check whether drawTriangle is of type UITableView:
In Swift 3,
if drawTriangle is UITableView{
// in deed drawTriangle is UIView
// do something here...
} else{
// do something here...
}
This also could be used for classes defined by yourself. You could use this to check subviews of a view.
Just for the sake of completeness based on the accepted answer and some others:
let items : [Any] = ["Hello", "World", 1]
for obj in items where obj is String {
// obj is a String. Do something with str
}
But you can also (compactMap
also "maps" the values which filter
doesn't):
items.compactMap { $0 as? String }.forEach{ /* do something with $0 */ ) }
And a version using switch
:
for obj in items {
switch (obj) {
case is Int:
// it's an integer
case let stringObj as String:
// you can do something with stringObj which is a String
default:
print("\(type(of: obj))") // get the type
}
}
But sticking to the question, to check if it's an array (i.e. [String]
):
let items : [Any] = ["Hello", "World", 1, ["Hello", "World", "of", "Arrays"]]
for obj in items {
if let stringArray = obj as? [String] {
print("\(stringArray)")
}
}
Or more generally (see this other question answer):
for obj in items {
if obj is [Any] {
print("is [Any]")
}
if obj is [AnyObject] {
print("is [AnyObject]")
}
if obj is NSArray {
print("is NSArray")
}
}
as?
won't always give you the expected result because as
doesn't test if a data type is of a specific kind but only if a data type can be converted to or represented as specific kind.
Consider this code for example:
func handleError ( error: Error ) {
if let nsError = error as? NSError {
Every data type conforming to the Error
protocol can be converted to a NSError
object, so this will always succeed. Yet that doesn't mean that error
is in fact a NSError
object or a subclass of it.
A correct type check would be:
func handleError ( error: Error ) {
if type(of: error) == NSError.self {
However, this checks for the exact type only. If you want to also include subclasses of NSError
, you should use:
func handleError ( error: Error ) {
if error is NSError.Type {
Why not use the built in functionality built especially for this task?
let myArray: [Any] = ["easy", "as", "that"]
let type = type(of: myArray)
Result: "Array<Any>"
Be warned about this:
var string = "Hello" as NSString
var obj1:AnyObject = string
var obj2:NSObject = string
print(obj1 is NSString)
print(obj2 is NSString)
print(obj1 is String)
print(obj2 is String)
All of the four last lines return true, this is because if you type
var r1:CGRect = CGRect()
print(r1 is String)
... it prints "false" of course, but a Warning says that the Cast from CGRect to String fails. So some type are bridged, ans the 'is' keyword calls an implicit cast.
You should better use one of these:
myObject.isKind(of: MyClass.self))
myObject.isMember(of: MyClass.self))
myObject as? String
returns nil
if myObject
is not a String
. Otherwise, it returns a String?
, so you can access the string itself with myObject!
, or cast it with myObject! as String
safely.
If you just want to check the class without getting a warning because of the unused defined value (let someVariable ...), you can simply replace the let stuff with a boolean:
if (yourObject as? ClassToCompareWith) != nil {
// do what you have to do
}
else {
// do something else
}
Xcode proposed this when I used the let way and didn't use the defined value.
Why not to use something like this
fileprivate enum types {
case typeString
case typeInt
case typeDouble
case typeUnknown
}
fileprivate func typeOfAny(variable: Any) -> types {
if variable is String {return types.typeString}
if variable is Int {return types.typeInt}
if variable is Double {return types.typeDouble}
return types.typeUnknown
}
in Swift 3.
Swift 4.2 , In my case , using isKind function .
isKind(of:) Returns a Boolean value that indicates whether the receiver is an instance of given class or an instance of any class that inherits from that class.
let items : [AnyObject] = ["A", "B" , ... ]
for obj in items {
if(obj.isKind(of: NSString.self)){
print("String")
}
}
Readmore https://developer.apple.com/documentation/objectivec/nsobjectprotocol/1418511-iskind
Swift 3:
class Shape {}
class Circle : Shape {}
class Rectangle : Shape {}
if aShape.isKind(of: Circle.self) {
}
Swift 5.2 & Xcode Version:11.3.1(11C504)
Here is my solution of checking data type:
if let typeCheck = myResult as? [String : Any] {
print("It's Dictionary.")
} else {
print("It's not Dictionary.")
}
I hope it will help you.
If you have Response Like This:
{
"registeration_method": "email",
"is_stucked": true,
"individual": {
"id": 24099,
"first_name": "ahmad",
"last_name": "zozoz",
"email": null,
"mobile_number": null,
"confirmed": false,
"avatar": "http://abc-abc-xyz.amazonaws.com/images/placeholder-profile.png",
"doctor_request_status": 0
},
"max_number_of_confirmation_trials": 4,
"max_number_of_invalid_confirmation_trials": 12
}
and you want to check for value is_stucked
which will be read as AnyObject, all you have to do is this
if let isStucked = response["is_stucked"] as? Bool{
if isStucked{
print("is Stucked")
}
else{
print("Not Stucked")
}
}
If you don't know that you will get an array of dictionaries or single dictionary in the response from server you need to check whether the result contains an array or not.
In my case always receiving an array of dictionaries except once. So, to handle that I used the below code for swift 3.
if let str = strDict["item"] as? Array<Any>
Here as? Array checks whether the obtained value is array (of dictionary items). In else case you can handle if it is single dictionary item which is not kept inside an array.
let originalArray : [Any?] = ["Hello", "World", 111, 2, nil, 3.34]
let strings = originalArray.compactMap({ $0 as? String })
print(strings)
//printed: ["Hello", "World"]