A package to provide detailed description of any structure.
CustomDetailedStringConvertible
serves as an alternative to Mirror
. This protocol describes the details to types confirming to CustomDetailedStringConvertible
without exposing implementation details of Swift Foundation structures that Mirror
would.
To use this package, your structure needs to conform to the CustomDetailedStringConvertible
protocol.
struct BasicModel: CustomDetailedStringConvertible {
let name: String
let age: Int
func detailedDescription(using descriptor: DetailedDescription.Descriptor<BasicModel>) -> any DescriptionBlockProtocol {
descriptor.container {
descriptor.value(for: \.name)
descriptor.value(for: \.age)
}
}
}
let model = BasicModel(name: "hello", age: 100)
detailedPrint(model)
// BasicModel
// ├─name: "hello"
// ╰─age: 100
DetailedDescription
uses Swift Package Manager as its build tool. If you want to import in your own project, it's as simple as adding a dependencies
clause to your Package.swift
:
dependencies: [
.package(url: "https://github.com/Vaida12345/DetailedDescription.git", from: "1.0.0")
]
and then adding the appropriate module to your target dependencies.
You can add this framework as a dependency to your Xcode project by clicking File -> Swift Packages -> Add Package Dependency. The package is located at:
https://github.com/Vaida12345/DetailedDescription
Full Documentation in DocC. View on Github Pages
Arguably the best use case is when dealing with recursive structures.
Definition | Result |
---|---|
struct SCNNodeDescriptor: CustomDetailedStringConvertible {
let node: SCNNode
func detailedDescription(
using descriptor: DetailedDescription.Descriptor
) -> any DescriptionBlockProtocol {
descriptor.container {
descriptor.value("name", of: node.name)
descriptor.sequence("children",
of: node.childNodes.map(SCNNodeDescriptor.init)
)
}
}
} |
SCNNodeDescriptor ├─name: nil ╰─children: <1 element> ╰─[0]: SCNNodeDescriptor ├─name: "scene" ╰─children: <1 element> ╰─[0]: SCNNodeDescriptor ├─name: "Meshes" ╰─children: ... |
With the above definition, you can now inspect a complex SCNNode
by detailedPrint(SCNNodeDescriptor(node: node))
.
Similar to SwiftUI
, the detailedDescription
function also supports builing conditional blocks, and the use of loops.
func detailedDescription(using descriptor: DetailedDescription.Descriptor<Component>) -> any DescriptionBlockProtocol {
descriptor.container(showType: false) {
if !content.isEmpty {
descriptor.value(for: \.content)
}
descriptor.sequence(for: \.metadata, hideEmptySequence: true)
descriptor.value(for: \.boundary)
}
}
In the above example, the existence of content
in its output is conditional, and appears only when it is not empty.
It also supports complex block-building. The following is a portion of code for exploring the PDF structure using PDFKit
descriptor.container("CGPDFArray") {
descriptor.forEach(0..<count) { index in
if let innerArray = source._arrayGetValue(using: CGPDFArrayGetArray, index: index) {
descriptor.value("", of: CGPDFArrayWrapper(source: innerArray))
} else if let name = source._arrayGetValue(using: CGPDFArrayGetName, index: index) {
descriptor.value("", of: String(cString: name))
} else if let stream = source._arrayGetValue(using: CGPDFArrayGetStream, index: index) {
descriptor.value("", of: stream.dictionary)
} else if let dictionary = source._arrayGetValue(using: CGPDFArrayGetDictionary, index: index) {
descriptor.value("", of: dictionary)
} else {
descriptor.string("(unknown)")
}
}
}
The container
comes with ways to configure how you want to describe the children, including showType
.
struct BasicModel: CustomDetailedStringConvertible {
let name: String
let age: Int
func detailedDescription(using descriptor: DetailedDescription.Descriptor<BasicModel>) -> any DescriptionBlockProtocol {
descriptor.container(showType: true) {
descriptor.container("details", showType: false) {
descriptor.value(for: \.name)
descriptor.value(for: \.age)
}
descriptor.value(for: \.name)
}
}
}
Similar to SwiftUI
environment values, values are effected by the innermost definition of showType
, and child containers inherit parent configuration if not specified.
BasicModel
├─details
│ ├─name: "dog"
│ ╰─age: 11
╰─name: "dog" <String>