Does Swift support reflection?
Asked Answered
P

6

116

Does Swift support reflection? e.g. is there something like valueForKeyPath: and setValue:forKeyPath: for Swift objects?

Actually does it even have a dynamic type system, something like obj.class in Objective-C?

Pocosin answered 5/6, 2014 at 12:48 Comment(2)
I created a helper class for reflection in Swift. You can find it at: github.com/evermeer/EVReflectionEchevarria
They have removed reflect within Swift 2.0. This is how I am enumerating attributes and values LinkPropose
B
86

Looks like there's the start of some reflection support:

class Fruit {
    var name="Apple"
}

reflect(Fruit()).count         // 1
reflect(Fruit())[0].0          // "name"
reflect(Fruit())[0].1.summary  // "Apple"

From mchambers gist, here: https://gist.github.com/mchambers/fb9da554898dae3e54f2

Baronial answered 5/6, 2014 at 20:40 Comment(6)
Well, I wouldn't consider this a real reflection. For one thing, it's readonly. It seems to me it's just a hack to enable debugging in Xcode. Protocol Mirror actually quotes the word IDE several times.Stifling
And it works only for properties. No method reflection.Stifling
Author of the gist checking in. I wrote this in the Swift lab at WWDC, figured I'd share the rest of it. As everybody's figured out, the engineers I spoke with confirmed the reflect() function exists to support the Playground. But you can still have some fun with it :) here i've hacked out a little model serializer using it. Paste it into the Playground and have fun: gist.github.com/mchambers/67640d9c3e2bcffbb1e2Fricassee
Have a look the answer https://mcmap.net/q/182058/-does-swift-support-reflection to find out how _stdlib_getTypeName can help.Jackstay
I can't get this to work when subclassing UIView for instance. Any idea?Leoni
Here is a class that will do reflection of base classes and optionals (not types) and has support for NSCoding and parsing from and to a dictionary: github.com/evermeer/EVCloudKitDao/blob/master/AppMessage/…Echevarria
E
46

If a class extends NSObject, then all of Objective-C's introspection and dynamism works. This includes:

  • The ability to ask a class about its methods and properties, and to invoke methods or set properties.
  • The ability to exchange method implementations. (add functionality to all instances).
  • The ability to generate and assign a new sub-class on the fly. (add functionality to a given instance)

One shortcoming of this functionality is support for Swift optional value types. For example Int properties can be enumerated and modified but Int? properties cannot. Optional types can be enumerated partially using reflect/MirrorType, but still not modified.

If a class does not extend NSObject, then only the new, very limited (and in progress?) reflection works (see reflect/MirrorType), which adds limited ability to ask a instance about its class and properties, but none of the additional features above.

When not extending NSObject, or using the '@objc' directive, Swift defaults to static- and vtable-based dispatch. This is faster, however, in the absence of a virtual machine does not allow runtime method interception. This interception is a fundamental part of Cocoa and is required for the following types of features:

  • Cocoa's elegant property observers. (Property observers are baked right in to the Swift language).
  • Non-invasively applying cross-cutting concerns like logging, transaction management (i.e Aspect Oriented Programming).
  • Proxies, message forwarding, etc.

Therefore its recommended that clases in Cocoa/CocoaTouch applications implemented with Swift:

  • Extend from NSObject. The new class dialog in Xcode steers in this direction.
  • Where the overhead of of a dynamic dispatch leads to performance issues, then static dispatch can be used - in tight loops with calls to methods with very small bodies, for example.

Summary:

  • Swift can behave like C++, with fast static/vtable dispatch and limited reflection. This makes it suitable for lower level or performance intensive applications, but without the complexity, learning curve or risk of error associated with C++
  • While Swift is a compiled language, the messaging style of method invocation adds the introspection and dynamism found in modern languages like Ruby and Python, just like Objective-C, but without Objective-C's legacy syntax.

Reference data: Execution overhead for method invocations:

  • static : < 1.1ns
  • vtable : ~ 1.1ns
  • dynamic : ~4.9ns

(actual performance depends on hardware, but the ratios will remain similar).

Also, the dynamic attribute allows us to explicitly instruct Swift that a method should use dynamic dispatch, and will therefore support interception.

public dynamic func foobar() -> AnyObject {
}
Ecclesia answered 6/6, 2014 at 1:12 Comment(1)
Even using Objective-C techniques it seems to not work for optional Swift types. I would suggest noting this limitation in the answer unless I'm missing a trick.Gatecrasher
S
8

The documentation speaks about a dynamic type system, mainly about

Type and dynamicType

See Metatype Type (in Language Reference)

Example:

var clazz = TestObject.self
var instance: TestObject = clazz()

var type = instance.dynamicType

println("Type: \(type)") //Unfortunately this prints only "Type: Metatype"

Now assuming TestObject extends NSObject

var clazz: NSObject.Type = TestObject.self
var instance : NSObject = clazz()

if let testObject = instance as? TestObject {
    println("yes!") //prints "yes!"
}

Currently, there is no reflection implemented.

EDIT: I was apparently wrong, see stevex's answer. There is some simple readonly reflection for properties build in, probably to allow IDEs to inspect object contents.

Stifling answered 5/6, 2014 at 12:56 Comment(0)
B
6

No reflect keyword in Swift 5, now you can use

struct Person {
    var name="name"
    var age = 15
}

var me = Person()
var mirror = Mirror(reflecting: me)

for case let (label?, value) in mirror.children {
    print (label, value)
}

Bloem answered 27/2, 2020 at 2:19 Comment(1)
Why is this not upvoted? This is v useful . I'm going to apply it for json deserializationPreemie
J
5

It seems that a Swift reflection API is not a high priority for Apple at the moment. But besides @stevex answer there is another function in the standard library that helps.

As of beta 6 _stdlib_getTypeName gets the mangled type name of a variable. Paste this into an empty playground:

import Foundation

class PureSwiftClass {
}

var myvar0 = NSString() // Objective-C class
var myvar1 = PureSwiftClass()
var myvar2 = 42
var myvar3 = "Hans"

println( "TypeName0 = \(_stdlib_getTypeName(myvar0))")
println( "TypeName1 = \(_stdlib_getTypeName(myvar1))")
println( "TypeName2 = \(_stdlib_getTypeName(myvar2))")
println( "TypeName3 = \(_stdlib_getTypeName(myvar3))")

The output is:

TypeName0 = NSString
TypeName1 = _TtC13__lldb_expr_014PureSwiftClass
TypeName2 = _TtSi
TypeName3 = _TtSS

Ewan Swick's blog entry helps to decipher these strings:

e.g. _TtSi stands for Swift's internal Int type.

Mike Ash has a great blog entry covering the same topic.

Jackstay answered 17/8, 2014 at 1:11 Comment(4)
@aleclarson Yes, that's also quite useful.Jackstay
aren't those private API? Will Apple approve the app if they are used?Disbelieve
@EduardoCosta yes, for sure. They are private. I only use them for debug builds.Jackstay
Here's an updated link to Ewan Swick's blog article: eswick.com/2014/06/08/Inside-SwiftDialect
L
5

You might want to consider using toString() instead. It is public and works just the same as _stdlib_getTypeName() with the difference that it also works on AnyClass, e.g. in a Playground enter

class MyClass {}

toString(MyClass.self) // evaluates to "__lldb_expr_49.MyClass"
Leonoraleonore answered 8/3, 2015 at 15:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.