I want to store objects of different types in an array. The program below is only a minimum demo. In the anyArray:[Any] an instance of Object1 is stored. The print statement prints out the expected object type. In the following line the test of the stored object's type returns true. This means, during run time the correct object type is known and every thing seems to be fine.
class Object1 {
var name = "Object1"
}
var anyArray:[Any] = [Object1()]
print("\(type(of: anyArray[0]))")
let testResult = anyArray[0] is Object1
print("Test result:\(testResult)")
//print("Name:\((anyArray[0]).name)")
Console output:
Object1
Test result:true
However, if I try to print out the name property of the object, I get an error message from the editor:
Value of type 'Any' has no member 'name'
Well, at compile time the object's type is unknown. That's why the compiler complains. How can I tell the compiler that it is OK to access the properties of the stored object?
The difference comes from the difference from Type Checking in:
The
is
operator checks at runtime whether the expression can be cast to the specified type.type(of:)
checks, at runtime, the exact type, without consideration for subclasses.anyArray[0].name
doesn't compile since the TypeAny
doesn't have aname
property.If you're sure
anyArray[0]
is anObject1
, you could use the downcast operatoras!
:To check at runtime if an element from
anyArray
could be anObject1
use optional binding, using the conditional casting operatoras?
:if let:
Or use the
guard
statement, if you want to use that object in the rest of the scope:If all objects in your array have a
name
property, and you don't want to go through all the hoops of optional binding repeatedly, then use a protocol. Your code will look like this:Notice that
anyArray
is now an array ofNamed
objects, and thatObject1
conforms to theNamed
protocol.To learn more about protocols, have a look here.