SwiftPrettyPrint
SwiftPrettyPrint gives Human-readable outputs than print()
, debugPrint()
and dump()
in Swift standard library.
๐
Features - Style
- Single-line
- Multi-line
- Integration
- LLDB
- Terminal
- Combine
- RxSwift
- Package Manager
- Swift Package Manager
- CocoaPods
- Carthage
- OS Support
- Apple platforms
- Linux
- Windows
- SwiftUI Property-wrapper
-
@AppStorage
-
@Binding
-
@Environment
-
@EnvironmentObject
-
@FetchRequest
(Property-wrapper name only) -
@FocusedBinding
-
@FocusedState
(Property-wrapper name only) -
@FocusedValue
-
@GestureState
-
@Namespace
-
@ObservedObject
-
@Published
-
@ScaledMetric
-
@SceneStorage
(Support types are limited onlyURL
,Int
,Double
,String
andBool
) -
@State
-
@StateObject
-
@UIApplicationDelegateAdaptor
(Property-wrapper name only) -
@NSApplicationDelegateAdaptor
(Property-wrapper name only)
-
Table of Contents ๐
- Motivation
๐ช - API
- Operator-based API
- Format options
- Integrations
๐ - Installation
- Recommend Settings
๐ - Requirements
- Development
- Author
๐ช
Motivation The print()
, debugPrint()
and dump()
are implemented in standard library of Swift.
But outputs of these functions are difficult to read sometimes.
For example, there are following types and a value:
enum Enum {
case foo(Int)
}
struct ID {
let id: Int
}
struct Struct {
var array: [Int?]
var dictionary: [String: Int]
var tuple: (Int, string: String)
var `enum`: Enum
var id: ID
}
let value = Struct(array: [1, 2, nil],
dictionary: ["one": 1, "two": 2],
tuple: (1, string: "string"),
enum: .foo(42),
id: ID(id: 7))
Use Standard library of Swift
When you use the standard library, you get the following results.
print(value)
// Struct(array: [Optional(1), Optional(2), nil], dictionary: ["one": 1, "two": 2], tuple: (1, string: "string"), enum: SwiftPrettyPrintExample.Enum.foo(42), id: SwiftPrettyPrintExample.ID(id: 7))
debugPrint(value)
// SwiftPrettyPrintExample.Struct(array: [Optional(1), Optional(2), nil], dictionary: ["one": 1, "two": 2], tuple: (1, string: "string"), enum: SwiftPrettyPrintExample.Enum.foo(42), id: SwiftPrettyPrintExample.ID(id: 7))
dump(value)
// โฟ SwiftPrettyPrintExample.Struct
// โฟ array: 3 elements
// โฟ Optional(1)
// - some: 1
// โฟ Optional(2)
// - some: 2
// - nil
// โฟ dictionary: 2 key/value pairs
// โฟ (2 elements)
// - key: "one"
// - value: 1
// โฟ (2 elements)
// - key: "two"
// - value: 2
// โฟ tuple: (2 elements)
// - .0: 1
// - string: "string"
// โฟ enum: SwiftPrettyPrintExample.Enum.foo
// - foo: 42
// โฟ id: SwiftPrettyPrintExample.ID
// - id: 7
These outputs are enough informations for debugging, but not human-readable outputs.
Use SwiftPrettyPrint
With the SwiftPrittyPrint, it looks like this:
Pretty.print(value)
// Struct(array: [1, 2, nil], dictionary: ["one": 1, "two": 2], tuple: (1, string: "string"), enum: .foo(42), id: 7)
Pretty.prettyPrint(value)
// Struct(
// array: [
// 1,
// 2,
// nil
// ],
// dictionary: [
// "one": 1,
// "two": 2
// ],
// tuple: (
// 1,
// string: "string"
// ),
// enum: .foo(42),
// id: 7
// )
Of course, we also can use the SwiftPrettyPrint with LLDB.
(By using LLDB integration, you can use it with shorter keywords such as _p
and _pp
)
(lldb) e Pretty.prettyPrint(value)
Struct(
array: [
1,
2,
nil
],
dictionary: [
"one": 1,
"two": 2
],
tuple: (
1,
string: "string"
),
enum: .foo(42),
id: 7
)
API
SwiftPrettyPrint has four basic functions as follows:
print(label: String?, _ targets: Any..., separator: String, option: Pretty.Option)
- print in one-line.
prettyPrint(label: String?, _ targets: Any..., separator: String, option: Pretty.Option)
- print in multiline.
printDebug(label: String?, _ targets: Any..., separator: String, option: Pretty.Option)
- print in one-line with type-information.
prettyPrintDebug(label: String?, _ targets: Any..., separator: String, option: Pretty.Option)
- print in multiline with type-information.
The only required argument is targets
, it can usually be described as follows.
let array: [URL?] = [
URL(string: "https://github.com/YusukeHosonuma/SwiftPrettyPrint"),
nil
]
Pretty.print(array)
// => [https://github.com/YusukeHosonuma/SwiftPrettyPrint, nil]
Pretty.prettyPrint(array)
// =>
// [
// https://github.com/YusukeHosonuma/SwiftPrettyPrint,
// nil
// ]
Pretty.printDebug(array)
// => [Optional(URL("https://github.com/YusukeHosonuma/SwiftPrettyPrint")), nil]
Pretty.prettyPrintDebug(array)
// =>
// [
// Optional(URL("https://github.com/YusukeHosonuma/SwiftPrettyPrint")),
// nil
// ]
Operator-based API
You can use operator based alias APIs that like Ruby.
This isn't needed to enclose in parentheses that convenient to long expression.
p >>> 42
// => 42
p >>> 42 + 2 * 4 // It can also be applied to expression
// => 50
p >>> String(string.reversed()).hasSuffix("eH")
// => true
pp >>> ["Hello", "World"]
// =>
// [
// "Hello",
// "World"
// ]
Operator syntax | Equatable to |
---|---|
p >>> 42 |
Pretty.print(42) |
pp >>> 42 |
Pretty.prettyPrint(42) |
pd >>> 42 |
Pretty.printDebug(42) |
ppd >>> 42 |
Pretty.prettyPrintDebug(42) |
Format options
You can configure format options, shared or passed by arguments.
Indent size
You can specify indent size in pretty-print like following:
// Global option
Pretty.sharedOption = Pretty.Option(indentSize: 4)
let value = (bool: true, array: ["Hello", "World"])
// Use `sharedOption`
Pretty.prettyPrint(value)
// =>
// (
// bool: true,
// array: [
// "Hello",
// "World"
// ]
// )
// Use option that is passed by argument
Pretty.prettyPrint(value, option: Pretty.Option(prefix: nil, indentSize: 2))
// =>
// (
// bool: true,
// array: [
// "Hello",
// "World"
// ]
// )
colorized
Output strings can be ANSI colored.
The options for coloring are specified as follows:
Pretty.sharedOption = Pretty.Option(colored: true)
Under this configuration, the following outputs can be achieved in AppCode:
It works only on console that ANSI color supported (e.g. AppCode, Terminal.app). This does not includes Xcode debug console.
See also Terminal section.
Prefix and Label
You can specify a global prefix and a label (e.g. variable name) like following:
Pretty.sharedOption = Pretty.Option(prefix: "[DEBUG]")
let array = ["Hello", "World"]
Pretty.print(label: "array", array)
// => [DEBUG] array: ["Hello", "World"]
Pretty.p("array") >>> array
// => [DEBUG] array: ["Hello", "World"]
Outputting in Console.app
Applying .osLog
to Option.outputStrategy
makes the output be shown in Console.app
:
The outputs in xcode-debug-console will be the following.
Debug.sharedOption = Debug.Option(outputStrategy: .osLog)
let dog = Dog(id: DogId(rawValue: "pochi"), price: Price(rawValue: 10.0), name: "ใใ")
Debug.print(dog)
// => 2020-04-02 11:51:10.766231+0900 SwiftPrettyPrintExample[41397:2843004] Dog(id: "pochi", price: 10.0, name: "ใใ")
๐
Integrations LLDB
Please copy and add follows to your ~/.lldbinit
(please create the file if the file doesn't exist):
command regex _p 's/(.+)/e -l swift -o -- var option = Pretty.sharedOption; option.prefix = nil; Pretty.print(%1, option: option)/'
command regex _pp 's/(.+)/e -l swift -o -- var option = Pretty.sharedOption; option.prefix = nil; Pretty.prettyPrint(%1, option: option)/'
or install via lowmad:
$ lowmad install https://github.com/YusukeHosonuma/SwiftPrettyPrint.git
Note:
If you already installed 1.1.0 or older version of SwiftPrettyPrint via lowmad, please remove scripts manually before update. (e.g. rm /usr/local/lib/lowmad/commands/YusukeHosonuma-SwiftPrettyPrint/swift_pretty_print.py
)
This lets you to use the lldb command in debug console as follows:
(lldb) e -l swift -- import SwiftPrettyPrint # If needed
(lldb) _p dog
Dog(id: "pochi", price: 10.0, name: "ใใ")
(lldb) _pp dog
Dog(
id: "pochi",
price: 10.0,
name: "ใใ"
)
Terminal
SwiftPrettyPrint outputs log files to the following files automatically when running iOS Simulator or macOS.
- /tmp/SwiftPrettyPrint/output.log
- /tmp/SwiftPrettyPrint/output-colored.log (ANSI colored)
So you can read them from other tools such as tail
or grep
and others.
$ tail -F /tmp/SwiftPrettyPrint/output-colored.log
A output-colored.log
is ANSI colorlized, so this looks beautiful on terminal.
Customize
You can customize terminal ANSI colors by Debug.Option.theme
property like as follows.
let theme = ColorTheme(
type: { $0.green().bold() },
nil: { $0.yellow() },
bool: { $0.yellow() },
string: { $0.blue() },
number: { $0.cyan() },
url: { $0.underline() }
)
Debug.sharedOption = Debug.Option(theme: theme)
ANSI colors can be easily specified using ColorizeSwift.
Did you create a beautiful theme?
Please add new theme to ColorTheme.swift and create PR.
public struct ColorTheme {
...
+ public static let themeName = ColorTheme(
+ type: { ... },
+ nil: { ... },
+ bool: { ... },
+ string: { ... },
+ number: { ... },
+ url: { ... }
+ )
public var type: (String) -> String
public var `nil`: (String) -> String
...
Thanks!
SwiftUI
You can use prettyPrint()
and prettyPrintDebug()
on any View
.
// Standard API.
Text("Swift")
.prettyPrint()
.prettyPrintDebug()
// You can specify label if needed.
Text("Swift")
.prettyPrint(label: "๐")
.prettyPrintDebug(label: "๐")
This extension is useful to examine the internal structure.
Combine
You can use prettyPrint()
operator in Combine framework.
[[1, 2], [3, 4]]
.publisher
.prettyPrint("๐")
.sink { _ in }
.store(in: &cancellables)
// =>
// ๐: receive subscription: [[1, 2], [3, 4]]
// ๐: request unlimited
// ๐: receive value:
// [
// 1,
// 2
// ]
// ๐: receive value:
// [
// 3,
// 4
// ]
// ๐: receive finished
You can specify when:
and format:
.
[[1, 2], [3, 4]]
.publisher
.prettyPrint("๐", when: [.output, .completion], format: .singleline)
.sink { _ in }
.store(in: &cancellables)
// =>
// ๐: receive value: [1, 2]
// ๐: receive value: [3, 4]
// ๐: receive finished
You can use alias API p()
and pp()
too.
[[1, 2], [3, 4]]
.publisher
.p("๐") // Output as single-line
.pp("๐") // Output as multiline
.sink { _ in }
.store(in: &cancellables)
Installation
CocoaPods (Recommended)
pod "SwiftPrettyPrint", "~> 1.2.0", :configuration => "Debug" # enabled on `Debug` build only
The example app is here.
Carthage
github "YusukeHosonuma/SwiftPrettyPrint"
Swift Package Manager
Add the following line to the dependencies in your Package.swift
file:
.package(url: "https://github.com/YusukeHosonuma/SwiftPrettyPrint.git", .upToNextMajor(from: "1.2.0"))
Finally, include "SwiftPrettyPrint" as a dependency for your any target:
let package = Package(
// name, platforms, products, etc.
dependencies: [
.package(url: "https://github.com/YusukeHosonuma/SwiftPrettyPrint.git", .upToNextMajor(from: "1.2.0")),
// other dependencies
],
targets: [
.target(name: "<your-target-name>", dependencies: ["SwiftPrettyPrint"]),
// other targets
]
)
Alternatively, use Xcode integration. This function is available since Xcode 10.
๐
Recommend Settings If you don't want to write an import
statement when debugging.
We recommend to create Debug.swift
and to declaration any type as typealias
like following:
// Debug.swift
#if canImport(SwiftPrettyPrint)
import SwiftPrettyPrint
typealias Debug = SwiftPrettyPrint.Pretty // You can use short alias such as `D` too.
#endif
You don't need to write a import
statement in each sources any longer.
// AnySource.swift
Debug.print(42)
Debug.prettyPrint(label: "array", array)
Note:
This can't be used to the operator-based API such as p >>>
. (This is a Swift language's limitation)
Requirements
- Xcode 11.3+ (Swift 5.1+)
- Platforms
- iOS 10.0+
- macOS 10.12+
- watchOS 5.0+
- tvOS 12.0+
Development
Require:
- Xcode 11.3.1
- Note: But run tests are failed on macOS 11.0.1, please use
make test
or latest version of Xcode to run unit tests.
- Note: But run tests are failed on macOS 11.0.1, please use
- pre-commit
Execute make setup
to install development tools to system (not include Xcode 11.3).
$ make help
setup Install requirement development tools to system and setup (not include Xcode 11.3)
build swift - build
test swift - test
xcode swift - generate xcode project
format format sources by SwiftFormat
lint cocoapods - lint podspec
release cocoapods - release
info cocoapods - show trunk information
Author
Developed by Penginmura.