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
isoperator 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].namedoesn't compile since the TypeAnydoesn't have anameproperty.If you're sure
anyArray[0]is anObject1, you could use the downcast operatoras!:To check at runtime if an element from
anyArraycould be anObject1use optional binding, using the conditional casting operatoras?:if let:
Or use the
guardstatement, if you want to use that object in the rest of the scope:If all objects in your array have a
nameproperty, 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
anyArrayis now an array ofNamedobjects, and thatObject1conforms to theNamedprotocol.To learn more about protocols, have a look here.