[swift] Checking if an object is 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?

This question is related to swift type-inference typechecking

The answer is


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.


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


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.


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")
 }
}

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:
        ...
    }

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")
}

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.


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.


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.


Swift 3:

class Shape {}
class Circle : Shape {}
class Rectangle : Shape {}

if aShape.isKind(of: Circle.self) {
}

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 subclass of NSError, you should use:

func handleError ( error: Error ) {
    if error is NSError.Type {

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"

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>"

for swift4:

if obj is MyClass{
    // then object type is MyClass Type
}