#1 Meaningful print debugging
Print debugging problems
Print debugging is simple and yet a powerful method. It is usually enough to toss a print()
in a couple of places and a problem is recognised. As it works well for values and simple objects, when we print a more complex instance, the result is less informing.
To illustrate this, let’s take this simple Wheel
class:
class Wheel {
var spokes:Int = 0
var diameter:Double = 0.0
init(spokes:Int = 32, diameter:Double = 26.0) {
self.spokes = spokes
self.diameter = diameter
}
func removeSpoke() {
spokes = spokes > 0 ? spokes-- : spokes
}
}
var wheel = Wheel(spokes: 36, diameter: 29)
print(wheel) // Wheel
debugPrint(wheel) //Wheel
Struct benefits
Note that if Wheel
was a struct and not a class we would already get a better print output:
var wheel = Wheel(spokes: 36, diameter: 29) // a struct
print(wheel) // Wheel(spokes: 36, diameter: 29)
debugPrint(wheel) // Wheel(spokes: 36, diameter: 29)
Protocols come to rescue
But we do have some classes and that’s where usually protocols CustomStringConvertible for print()
and CustomDebugStringConvertible for debugPrint()
come in handy:
extension Wheel:CustomStringConvertible, CustomDebugStringConvertible {
var description:String {
return "Wheel has \(spokes) spokes"
}
var debugDescription:String {
return "Wheel has \(spokes) spokes and \(diameter) inches"
}
}
So now, if we print our wheel object after confoming to CustomStringConvertible
and CustomDebugStringConvertible
protocols then we get:
print(wheel) //Wheel has 36 spokes
debugPrint(wheel) //Wheel has 36 spokes and 29.0 inches
Neat!
UIViewControllers and NSObject inheritance
One of the least verbose print outputs belongs to UIViewController
:
class WheelsViewController:UIViewController {
var wheels = [Wheel]()
}
var wheelsViewController = WheelsViewController()
print(wheelsViewController)//<WheelsViewController: 0x7f8e0861b590>
To make it simple, let’s just focus on CustomStringConvertible
protocol and print()
function. However, now if we try to conform to CustomStringConvertible
protocol:
extension WheelsViewController:CustomStringConvertible {
var description:String {
return "WheelsViewController has \(wheels)"
}
}
we receive an error “Redundant conformance of ‘WheelsViewController’ to protocol ‘CustomStringConvertible’“
That is because UIViewController
inherits from NSObject
that in turn conforms to NSObjectProtocol
. We can see that all NSObject
subclasses already implement description
and optionally debugDescription
properties:
public protocol NSObjectProtocol {
...
public var description: String { get }
optional public var debugDescription: String { get }
...
}
To fix that issue we will override inherited properties.
Overriding inherited properties
As we are not satisfied with the default implementation of description
property inherited from UIViewController
, we can override it within the body of our WheelsViewController
class or as an extension:
extension WheelsViewController {
override var description:String{
return "WheelsViewController has \(wheels.count) wheels"
}
}
var wheelsViewController = WheelsViewController()
print(wheelsViewController) //WheelsViewController has 0 wheels
wheelsViewController.wheels = [wheel1,wheel2]
print(wheelsViewController) //WheelsViewController has 2 wheels
That’s it!
This post is inspired by Udacity course Xcode Debugging. It’s a great starting point in mastering your debugging skills. More advanced developers will also find it useful. Playground with the code from this post is available here.