Trouble is, while String(describing: self) is the same as "\(ViewController.self)" at runtime, it doesn't yield the proper compile-time result. Is this two-method solution the best we've got?
Taking a step back, I gotta say that I’d not be confortable using String(describing:) in this way. Or "\(ViewController.self)" for that matter. To me these techniques seem to be closely tied to text output for debugging purposes, and thus shouldn’t be used at runtime. Consider this last quote from the String(describing:) documentation:
An unspecified result is supplied automatically by the Swift
standard library.
You could imagine a world where String(describing:) starts returning a slightly different value for class parameters, breaking your code at runtime [1].
In you’re shoes I’d use NSStringFromClass for this. This string is already used in storyboards (it’s how the nibs save a reference to the class of the objects they contain) and thus I’d expect it to not change format.
Share and Enjoy
Quinn “The Eskimo!” @ DTS @ Apple
[1] Currently this isn’t a problem because in Swift 4 the standard library is bundled with your app, and thus they can’t get out of sync. Once you update to Swift 5 that’ll no longer be the case.
I think NSStringFromClass is also not the desired solution for this problem. I'm using a similar pattern where I use the metatype to obtain the type name without the parent types.
Consider the following playground example:
class A {
class B {}
}
let a = NSStringFromClass(A.self)
let b = NSStringFromClass(A.B.self)
print(a, b, "\(A.self) \(A.B.self)")
This prints __lldb_expr_11.A _TtCC14__lldb_expr_111A1B A B. In my case I want to obtain just A and B and not need to parse/demangle some complex type string.
Playground are weird. In a real program this will print:
MyModule.A _TtCC8MyModule1A1B A B
Personally I think that MyModule.A is fine, while _TtCC8MyModule1A1B is definitely suboptimal (-: However, for the use case described by the original poster, nested classes are unlikely.
In my case I want to obtain just A and B …
Right, and that’s central to my point: There’s nothing about the String(describing:) API contract that guarantees those results. You could just as easily get back MyModule.A and MyModule.A.B.
The only way to nail down these values would be to change the API contract for String(describing:) or introduce a new API that does this specific thing.
I think we should go this route, because it will preserve source compatibility and from the users perspective we only get an official guarantee that metatypes will be converted as mentioned above and only the documentation will be updated. Behind the scenes we'd enforce the conversion and add some tests so that the behavior in further language evolution won't break this contract.
However, for the use case described by the original poster, nested classes are unlikely.
I actually want to use nested classes very much, but you can't use them with Interface Builder so I don't. I just name things with underscores instead of dots.
As for the API contract, I'm interested to know what people use the description of self available in default parameters for. If it's useless, and gets changed, I wonder if it would solve the forwarding problem.
Honestly, I’m not sure how I feel about this. The one thing I will say is that this is most definitely an ‘evolution’ thing, and that’s outside of my remit.