A package to provide detailed description of any structure.
DetailedStringConvertible serves as an alternative to Mirror. This protocol describes the details to types confirming to DetailedStringConvertible without exposing implementation details of Swift Foundation structures that Mirror would.
To use this package, your structure needs to conform to the DetailedStringConvertible protocol.
struct BasicModel: DetailedStringConvertible {
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: 100DetailedDescription 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: "2.0.3")
]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.git
Full Documentation in DocC. View on Github Pages
You can pass options to a DetailedStringConvertibleWithConfiguration, for example, FinderItem adopts this protocol, enabling you to pass different options when printing.
import DetailedDescription
let item = FinderItem(at: "/Users/vaida/Desktop/file.mid")
detailedPrint(item, configuration: .showFileSize)
// file.mid [25 kB]If you prefer not to import DetailedDescription on caller side, you can use debugDescription instead.
Arguably the best use case is when dealing with recursive structures.
| Definition | Result |
|---|---|
struct SCNNodeDescriptor: DetailedStringConvertible {
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 building conditional blocks, and the use of loops.
func detailedDescription(
using descriptor: DetailedDescription.Descriptor<Component>
) -> any DescriptionBlockProtocol {
descriptor.container {
if !content.isEmpty {
descriptor.value(for: \.content)
}
descriptor.sequence(for: \.metadata)
.hideEmptySequence()
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: DetailedStringConvertible {
let name: String
let age: Int
func detailedDescription(
using descriptor: DetailedDescription.Descriptor<BasicModel>
) -> any DescriptionBlockProtocol {
descriptor.container {
descriptor.container("details") {
descriptor.value(for: \.name)
descriptor.value(for: \.age)
}
.showType(false)
descriptor.value(for: \.name)
}
.showType()
}
}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>