As in Objective-C, where you can send arbitrary messages in id , arbitrary properties and methods can be called on the AnyObject instance in Swift. However, the data is different, and this is documented in Interacting with the Objective-C API in the "Using Swift with Cocoa and Objective-C" section.
Swift includes an AnyObject type that represents an object. This is similar to the Objective-C s id . Swift imports id as AnyObject , which allows you to write secure Swift code while maintaining the flexibility of an untyped object.
...
You can call any Objective-C method and access any property in the value of AnyObject without using a more specific class type. This includes Objective-C compatible methods and properties marked with the @objc attribute.
...
When you call a method with a value of type AnyObject , this method call behaves as an implicitly expanded optional. You can use the same optional chain syntax that you would use for additional methods in the protocols to call the method on AnyObject if necessary.
Here is an example:
func tryToGetTimeInterval(obj : AnyObject) { let ti = obj.timeIntervalSinceReferenceDate // NSTimeInterval! if let theTi = ti { print(theTi) } else { print("does not respond to `timeIntervalSinceReferenceDate`") } } tryToGetTimeInterval(NSDate(timeIntervalSinceReferenceDate: 1234)) // 1234.0 tryToGetTimeInterval(NSString(string: "abc")) // does not respond to `timeIntervalSinceReferenceDate`
obj.timeIntervalSinceReferenceDate - implicitly expanded optional and nil if the object does not have this property.
Here is an example of checking and calling a method:
func tryToGetFirstCharacter(obj : AnyObject) { let fc = obj.characterAtIndex // ((Int) -> unichar)! if let theFc = fc { print(theFc(0)) } else { print("does not respond to `characterAtIndex`") } } tryToGetFirstCharacter(NSDate(timeIntervalSinceReferenceDate: 1234)) // does not respond to `characterAtIndex` tryToGetFirstCharacter(NSString(string: "abc")) // 97
obj.characterAtIndex is an implicitly deployed optional closure. This code can be simplified with an additional chain:
func tryToGetFirstCharacter(obj : AnyObject) { if let c = obj.characterAtIndex?(0) { print(c) } else { print("does not respond to `characterAtIndex`") } }
In your case, TestClass does not have any @objc properties.
let xyz = typeAnyObject.xyz // error: value of type 'AnyObject' has no member 'xyz'
does not compile because the xyz property is unknown to the compiler.
let name = typeAnyObject.name
does compilation because - as you have noticed - NSException has a name property. The value, however, is nil because TestClass does not have an Objective-C compatible name method. As above, you should use an optional binding to safely expand the value (or check for nil ).
If your class is derived from NSObject
class TestClass : NSObject { var name : String? var xyz : String? }
then
let xyz = typeAnyObject.xyz
does a compilation. (Alternatively, mark the class or properties with @objc .) But now
let name = typeAnyObject.name // error: Ambigous use of `name`
no longer compiles. The reason is that both TestClass and NSException have a name property, but with different types ( String? Vs String ), so the type of this expression is ambiguous. This ambiguity can be resolved (optional) by dropping AnyObject back to TestClass :
if let name = (typeAnyObject as? TestClass)?.name { print(name) }
Output:
- You can call any method / property on an
AnyObject instance if that method / property of Objective-C is compatible. - You need to test the implicitly deployed option against
nil or use an optional binding to check if the instance is a Method / property. - Ambiguity arises if more than one class has (Objective-C) compatible methods with the same name but different types.
In particular, because of the last point, I would try to avoid this mechanism, if possible, and, perhaps, apply it to a well-known class instead (as in the last example).