What is the Swift equivalent of -[NSObject description]?

Swift

Swift Problem Overview


In Objective-C, one can add a description method to their class to aid in debugging:

@implementation MyClass
- (NSString *)description
{
    return [NSString stringWithFormat:@"<%@: %p, foo = %@>", [self class], foo _foo];
}
@end

Then in the debugger, you can do:

po fooClass
<MyClass: 0x12938004, foo = "bar">

What is the equivalent in Swift? Swift's REPL output can be helpful:

  1> class MyClass { let foo = 42 }
  2> 
  3> let x = MyClass()
x: MyClass = {
  foo = 42
}

But I'd like to override this behavior for printing to the console:

  4> println("x = \(x)")
x = C11lldb_expr_07MyClass (has 1 child)

Is there a way to clean up this println output? I've seen the Printable protocol:

/// This protocol should be adopted by types that wish to customize their
/// textual representation.  This textual representation is used when objects
/// are written to an `OutputStream`.
protocol Printable {
    var description: String { get }
}

I figured this would automatically be "seen" by println but it does not appear to be the case:

  1> class MyClass: Printable {
  2.     let foo = 42
  3.     var description: String { get { return "MyClass, foo = \(foo)" } }
  4. }   
  5> 
  6> let x = MyClass()
x: MyClass = {
  foo = 42
}
  7> println("x = \(x)")
x = C11lldb_expr_07MyClass (has 1 child)

And instead I have to explicitly call description:

 8> println("x = \(x.description)")
x = MyClass, foo = 42

Is there a better way?

Swift Solutions


Solution 1 - Swift

To implement this on a Swift type you must implement the CustomStringConvertible protocol and then also implement a string property called description.

For example:

class MyClass: CustomStringConvertible {
    let foo = 42

    var description: String {
        return "<\(type(of: self)): foo = \(foo)>"
    }
}

print(MyClass()) // prints: <MyClass: foo = 42>

Note: type(of: self) gets the type of the current instances instead of explicitly writing ‘MyClass’.

Solution 2 - Swift

Example of using CustomStringConvertible and CustomDebugStringConvertible protocols in Swift:

PageContentViewController.swift

import UIKit

class PageContentViewController: UIViewController {
    
    var pageIndex : Int = 0
    
    override var description : String { 
        return "**** PageContentViewController\npageIndex equals \(pageIndex) ****\n" 
    }
    
    override var debugDescription : String { 
        return "---- PageContentViewController\npageIndex equals \(pageIndex) ----\n" 
    }
    
            ...
}

ViewController.swift

import UIKit

class ViewController: UIViewController
{
    
    /*
        Called after the controller's view is loaded into memory.
    */
    override func viewDidLoad() {
        super.viewDidLoad()
 
        let myPageContentViewController = self.storyboard!.instantiateViewControllerWithIdentifier("A") as! PageContentViewController
        print(myPageContentViewController)       
        print(myPageContentViewController.description)
        print(myPageContentViewController.debugDescription)
    }
    
          ...
}

Which print out:

**** PageContentViewController
pageIndex equals 0 ****

**** PageContentViewController
pageIndex equals 0 ****

---- PageContentViewController
pageIndex equals 0 ----

Note: if you have a custom class which does not inherit from any class included in UIKit or Foundation libraries, then make it inherit of NSObject class or make it conform to CustomStringConvertible and CustomDebugStringConvertible protocols.

Solution 3 - Swift

Just use CustomStringConvertible and var description: String { return "Some string" }

works in Xcode 7.0 beta

class MyClass: CustomStringConvertible {
  var string: String?
  
  
  var description: String {
     //return "MyClass \(string)"
     return "\(self.dynamicType)"
  }
}

var myClass = MyClass()  // this line outputs MyClass nil

// and of course 
print("\(myClass)")

// Use this newer versions of Xcode
var description: String {
    //return "MyClass \(string)"
    return "\(type(of: self))"
}

Solution 4 - Swift

The answers relating to CustomStringConvertible are the way to go. Personally, to keep the class (or struct) definition as clean as possible, I would also separate out the description code into a separate extension:

class foo {
    // Just the basic foo class stuff.
    var bar = "Humbug!"
}

extension foo: CustomStringConvertible {
    var description: String {
        return bar
    }
}

let xmas = foo()
print(xmas)  // Prints "Humbug!"

Solution 5 - Swift

class SomeBaseClass: CustomStringConvertible {

    //private var string: String = "SomeBaseClass"

    var description: String {
        return "\(self.dynamicType)"
    }
    
    // Use this in newer versions of Xcode
    var description: String {
        return "\(type(of: self))"
    }
    
}

class SomeSubClass: SomeBaseClass {
    // If needed one can override description here

}


var mySomeBaseClass = SomeBaseClass()
// Outputs SomeBaseClass
var mySomeSubClass = SomeSubClass()
// Outputs SomeSubClass
var myOtherBaseClass = SomeSubClass()
// Outputs SomeSubClass

Solution 6 - Swift

As described here, you can also use Swift's reflection capabilities to make your classes generate their own description by using this extension:

extension CustomStringConvertible {
    var description : String {
        var description: String = "\(type(of: self)){ "
        let selfMirror = Mirror(reflecting: self)
        for child in selfMirror.children {
            if let propertyName = child.label {
                description += "\(propertyName): \(child.value), "
            }
        }
        description = String(description.dropLast(2))
        description += " }"
        return description
    }
}

Solution 7 - Swift

struct WorldPeace: CustomStringConvertible {
	let yearStart: Int
	let yearStop: Int

	var description: String {
		return "\(yearStart)-\(yearStop)"
	}
}

let wp = WorldPeace(yearStart: 2020, yearStop: 2040)
print("world peace: \(wp)")

// outputs:
// world peace: 2020-2040

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJasonView Question on Stackoverflow
Solution 1 - SwiftdrewagView Answer on Stackoverflow
Solution 2 - SwiftKing-WizardView Answer on Stackoverflow
Solution 3 - SwiftPeter AhlbergView Answer on Stackoverflow
Solution 4 - SwiftVince O'SullivanView Answer on Stackoverflow
Solution 5 - SwiftPeter AhlbergView Answer on Stackoverflow
Solution 6 - SwiftSir CodesalotView Answer on Stackoverflow
Solution 7 - SwiftneoneyeView Answer on Stackoverflow