Default reflection

Have you ever noticed how useless Swift’s class representations are? Value types automatically  show their members and values but classes don’t. What if you could add a simple way to automatically reflect those members, like with C2?Screen Shot 2016-04-18 at 12.55.01 PM

Mike Ash threw together an adorable solution that introduces a default reflection for any conforming class:

/// A reference type that provides a better default representation than the class name
public protocol DefaultReflectable: CustomStringConvertible {}

/// A default implementation that enables class members to display their values
extension DefaultReflectable {
    
    /// Constructs a better representation using reflection
    internal func DefaultDescription<T>(instance: T) -> String {
        let mirror = Mirror(reflecting: instance)
        let chunks = mirror.children.map({
            (label: String?, value: Any) -> String in
            if let label = label {
                if value is String {
                    return "\(label): \"\(value)\""
                }
                return "\(label): \(value)"
            } else {
                return "\(value)"
            }
        })
        if chunks.count > 0 {
            let chunksString = chunks.joinWithSeparator(", ")
            return "\(mirror.subjectType)(\(chunksString))"
        } else {
            return "\(instance)"
        }
    }
    
    /// Conforms to CustomStringConvertible
    public var description: String {
        return DefaultDescription(self)
    }
}

Unfortunately, you cannot (at least at this time) conform AnyObject and get this for free (“error: ‘AnyObject’ protocol cannot be extended”).

Manuel Carlos offers this:

3 Comments

  • Using `Streamable` instead of `CustomStringConvertible` can be apply to `AnyObject`. https://gist.github.com/norio-nomura/e1e0cff0fc7c3c80f65d5af7c56450e1

  • The class Mirror is like a good joke. First It’s not natural to use it. It’s not a swifty class. And second we can dynamically get value of properties but not set dynamically a value to a property. Because the reflection in swift is not good many frameworks try to mimic a reflection behaviour with layers of generic classes.

  • This falls over dead if you have a circular reference (such as a delegate) pointing back to a parent object.