A collection of tools for debugging, diffing, and testing your application's data structures.

Overview

Custom Dump

CI

A collection of tools for debugging, diffing, and testing your application's data structures.

Motivation

Swift comes with a wonderful tool for dumping the contents of any value to a string, and it's called dump. It prints all the fields and sub-fields of a value into a tree-like description:

struct User {
  var favoriteNumbers: [Int]
  var id: Int
  var name: String
}

let user = User(
  favoriteNumbers: [42, 1729],
  id: 2,
  name: "Blob"
)

dump(user)
▿ User
  ▿ favoriteNumbers: 2 elements
    - 42
    - 1729
  - id: 2
  - name: "Blob"

This is really useful, and can be great for building debug tools that visualize the data held in runtime values of our applications, but sometimes its output is not ideal.

For example, dumping dictionaries leads to a verbose output that can be hard to read (also note that the keys are unordered):

dump([1: "one", 2: "two", 3: "three"])
▿ 3 key/value pairs
  ▿ (2 elements)
    - key: 2
    - value: "two"
  ▿ (2 elements)
    - key: 3
    - value: "three"
  ▿ (2 elements)
    - key: 1
    - value: "one"

Similarly enums have a very verbose output:

dump(Result<Int, Error>.success(42))
▿ Swift.Result
   
    .success
  - success: 42

   

It gets even harder to read when dealing with deeply nested structures:

dump([1: Result<User, Error>.success(user)])
▿ 1 key/value pair
  ▿ (2 elements)
    - key: 1
    ▿ value: Swift.Result
   
    .success
      ▿ success: User
        ▿ favoriteNumbers: 2 elements
          - 42
          - 1729
        - id: 2
        - name: "Blob"

   

There are also times that dump simply does not print useful information, such as enums imported from Objective-C:

import UserNotifications

dump(UNNotificationSetting.disabled)
- __C.UNNotificationSetting

So, while the dump function can be handy, it is often too crude of a tool to use. This is the motivation for the customDump function.

customDump

The customDump function emulates the behavior of dump, but provides a more refined output of nested structures, optimizing for readability. For example, structs are dumped in a format that more closely mimics the struct syntax in Swift, and arrays are dumped with the indices of each element:

import CustomDump

customDump(user)
User(
  favoriteNumbers: [
    [0]: 42,
    [1]: 1729
  ],
  id: 2,
  name: "Blob"
)

Dictionaries are dumped in a more compact format that mimics Swift's syntax, and automatically orders the keys:

customDump([1: "one", 2: "two", 3: "three"])
[
  1: "one",
  2: "two",
  3: "three"
]

Similarly, enums also dump in a more compact, readable format:

customDump(Result<Int, Error>.success(42))
Result.success(42)

And deeply nested structures have a simplified tree-structure:

customDump([1: Result<User, Error>.success(user)])
[
  1: Result.success(
    User(
      favoriteNumbers: [
        [0]: 42,
        [1]: 1729
      ],
      id: 2,
      name: "Blob"
    )
  )
]

diff

Using the output of the customDump function we can build a very lightweight way to textually diff any two values in Swift:

var other = user
other.favoriteNumbers[1] = 91

print(diff(user, other)!)
  User(
    favoriteNumbers: [
      [0]: 42,
-     [1]: 1729
+     [1]: 91
    ],
    id: 2,
    name: "Blob"
  )

Further, extra work is done to minimize the size of the diff when parts of the structure haven't changed, such as a single element changing in a large collection:

let users = (1...5).map {
  User(
    favoriteNumbers: [$0],
    id: $0,
    name: "Blob \($0)"
  )
}

var other = users
other.append(
  .init(
    favoriteNumbers: [42, 1729],
    id: 100,
    name: "Blob Sr."
  )
)

print(diff(users, other)!)
  [
    … (4 unchanged),
+   [5]: User(
+     favoriteNumbers: [
+       [0]: 42,
+       [1]: 1729
+     ],
+     id: 100,
+     name: "Blob Sr."
+   )
  ]

For a real world use case we modified Apple's Landmarks tutorial application to print the before and after state when favoriting a landmark:

XCTAssertNoDifference

The XCTAssertEqual function from XCTest allows you to assert that two values are equal, and if they are not the test suite will fail with a message:

var other = user
other.name += "!"

XCTAssertEqual(user, other)
XCTAssertEqual failed: ("User(favoriteNumbers: [42, 1729], id: 2, name: "Blob")") is not equal to ("User(favoriteNumbers: [42, 1729], id: 2, name: "Blob!")")

Unfortunately this failure message is quite difficult to visually parse and understand. It takes a few moments of hunting through the message to see that the only difference is the exclamation mark at the end of the name. The problem gets worse if the type is more complex, consisting of nested structures and large collections.

This library also ships with an XCTAssertNoDifference function to mitigate these problems. It works like XCTAssertEqual except the failure message uses a nicely formatted diff to show exactly what is different between the two values:

XCTAssertNoDifference(user, other)
XCTAssertNoDifference failed: …

    User(
      favoriteNumbers: […],
      id: 2,
  −   name: "Blob"
  +   name: "Blob!"
    )

(First: −, Second: +)

Customization

Custom Dump provides a few important ways to customize how a data type is dumped: CustomDumpStringConvertible, CustomDumpReflectable, and CustomDumpRepresentable.

CustomDumpStringConvertible

The CustomDumpStringConvertible protocol provides a simple way of converting a type to a raw string for the purpose of dumping. It is most appropriate for types that have a simple, un-nested internal representation, and typically its output fits on a single line, for example dates, UUIDs, URLs, etc:

extension URL: CustomDumpStringConvertible {
  public var customDumpDescription: String {
    "URL(\(self.absoluteString))"
  }
}

customDump(URL(string: "https://www.pointfree.co/")!)
URL(https://www.pointfree.co/)

Custom Dump also uses this protocol internally to provide more useful output for enums imported from Objective-C:

import UserNotifications

print("dump:")
dump(UNNotificationSetting.disabled)
print("customDump:")
customDump(UNNotificationSetting.disabled)
dump:
- __C.UNNotificationSetting
customDump:
UNNotificationSettings.disabled

Encounter an Objective-C enum that doesn't print nicely? See the contributing section of this README to help submit a fix.

CustomDumpReflectable

The CustomDumpReflectable protocol provides a more comprehensive way of dumping a type into a more structured output. It allows you to construct a custom mirror that describes the structure that should be dumped. You can omit, add, and replace fields, or even change the "display style" of how the structure is dumped.

For example, let's say you have a struct representing state that holds a secure token in memory that should never be written to your logs. You can omit the token from customDump by providing a mirror that omits this field:

struct LoginState: CustomDumpReflectable {
  var username: String
  var token: String

  var customDumpMirror: Mirror {
    .init(
      self,
      children: [
        "username": self.username,
        // omit token from logs
      ],
      displayStyle: .struct
    )
  }
}

customDump(
  LoginState(
    username: "blob",
    token: "secret"
  )
)
LoginState(username: "blob")

And just like that, no token data will be written to the dump.

CustomDumpRepresentable

The CustomDumpRepresentable protocol allows you to return any value for the purpose of dumping. This can be useful to flatten the dump representation of wrapper types. For example, a type-safe identifier may want to dump its raw value directly:

struct ID: RawRepresentable {
  var rawValue: String
}

extension ID: CustomDumpRepresentable {
  var customDumpValue: Any {
    self.rawValue
  }
}

customDump(ID(rawValue: "deadbeef")
"deadbeef"

Contributing

There are many types in Apple's ecosystem that do not dump to a nicely formatted string. In particular, all enums that are imported from Objective-C dump to strings that are not very helpful:

import UserNotifications

dump(UNNotificationSetting.disabled)
- __C.UNNotificationSetting

For this reason we have conformed a bunch of Apple's types to the CustomDumpStringConvertible protocol so that they print out more reasonable descriptions. If you come across types that do not print useful information then we would happily accept a PR to conform those types to CustomDumpStringConvertible.

Installation

You can add Custom Dump to an Xcode project by adding it as a package dependency.

https://github.com/pointfreeco/swift-custom-dump

If you want to use Custom Dump in a SwiftPM project, it's as simple as adding it to a dependencies clause in your Package.swift:

dependencies: [
  .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "0.1.0")
]

Documentation

The latest documentation for the Custom Dump APIs is available here.

Other libraries

License

This library is released under the MIT license. See LICENSE for details.

Comments
  • Version 0.4.0 does not compile

    Version 0.4.0 does not compile

    CoreImage extensions with availability constraints result in the following error

    Protocol 'CustomDumpStringConvertible' requires 'customDumpDescription' to be available in iOS 9.0.0 and newer

    opened by chernousov-m 18
  • Improve dumping of Errors bridged to NSError

    Improve dumping of Errors bridged to NSError

    When dumping a Swift Error bridged to NSError, we'd only show the error domain and error code generated by the Swift runtime. We can get much better output (e.g., enum/case names, associated values, etc.) if we unwrap the Swift Error and dump it directly.

    opened by aroben 6
  • Add `if !os(WASI)` for SwiftWasm

    Add `if !os(WASI)` for SwiftWasm

    opened by inamiy 5
  • NSNumber crashes CustomDump

    NSNumber crashes CustomDump

    Dumping a struct with an NSNumber crashes CustomDump. Storing the number as Double fixes the issue for me. The NSNumber is coming out of CoreBluetooh's API so pretty old and low-level.

    opened by andreweades 5
  • Delete playground

    Delete playground

    We learned from iOS Folks that this what is killing Xcode's performance, and it seems to be true for Primer. Merging and maintaining a fork until it gets deleted in the official repo.

    opened by gonzalonunez 4
  • fix: Resolve Xcode 13 project startup beachball due to this playground

    fix: Resolve Xcode 13 project startup beachball due to this playground

    Motivation

    Our company's app is based on TCA with lots of SPM targets. When opening our app's Xcode project, the IDE will beachball for 5-10 seconds while on the "Packages. Finishing..." stage. We've experienced this hang across several Xcode versions, including 13.3.1 (13E500a).

    Fix

    After deleting or hiding this repo's playground, Xcode no longer hangs on startup while parsing SPM packages. This is similar to behavior reported in Swift.org forums.

    This PR moves the playground to a hidden directory and adds the playground to a workspace so it remains functional.

    opened by importRyan 4
  • UNNotificationPresentationOptions [.banner, .list] workaround

    UNNotificationPresentationOptions [.banner, .list] workaround

    Xcode 13.2 introduced a new build system that seems to have troubles with availability checks in getters.

    https://developer.apple.com/documentation/xcode-release-notes/xcode-13_2-release-notes

    Seeing ~30% speedup with this setting, it's kind of a bummer it can't be turned on with projects using TCA 😞

    Moving the check out of the getter fixes the compile issue.

    This solution may not be the PointFree way 😸

    opened by jaanussiim 4
  • Don't compile symbols for Linux that are not yet available

    Don't compile symbols for Linux that are not yet available

    Basically the same as #20 but for Linux this time. This PR fixes Swift 5.5 compilation on Linux.

    Backstory: Swift 5.5 images don't come bundled with a version of Foundation containing AttributedString yet. Whilst the changes are in the upstream branch, a release hasn't been made yet — I believe because macOS 12 is not out yet and Apple wants parity with "Apple Foundation".

    opened by davdroman 4
  • Xcode 13.2 Compile Error

    Xcode 13.2 Compile Error

    I have a project that depends on swift-composable-architecture which depends on this package. I am using the latest release, v0.30.0. After updating to Xcode 13.2 and doing a clean build, my project fails to compile.

    Please refer to the screenshots for the issue I am seeing.

    Screen Shot 2021-12-14 at 12 48 49 AM Screen Shot 2021-12-14 at 12 48 54 AM

    The Package.swift for my project declares:

     platforms: [
        .iOS(.v15)
      ],
    

    So I do not understand why I am encountering this issue.

    opened by romero-ios 3
  • Add new helper protocols to dump to control which child nodes are included in the dump

    Add new helper protocols to dump to control which child nodes are included in the dump

    Draft because it is based off of #58.

    Motivation

    The Current method of using CustomDumpReflectable can be used to include/exclude properties from the resulting dump but it requires implementing a custom mirror as described in https://github.com/pointfreeco/swift-custom-dump/blob/c9b6b940d95c0a925c63f6858943415714d8a981/Sources/CustomDump/CustomDumpReflectable.swift#L30. So for example if someone wants to exclude one property from an object with 20 properties, it will require creating a map with 19 properties to create a custom mirror. With CustomDumpExcludedChildNodesProvider the same result can be achieved by 2-3 lines and a simple string based array.

    Open to naming suggestions and improvements.

    opened by tahirmt 2
  • Add a XCTAssertNoDifference without Equatable requirement

    Add a XCTAssertNoDifference without Equatable requirement

    Hello @stephencelis 👋 ,

    It often happens we add structs's conformance to Equatable only for testing purpose without the need of having it for our business logic. I would personally find convenient (even if there is a small performance hit) if we could detect (or not) differences on structs without necessary adding the conformance to Equatable.

    Do you think this could fit the library or be convenient to other engineers? If not, feel free to reject the PR :)

    opened by adellibovi 2
  • Why ignore all the attributes of an attributed string?

    Why ignore all the attributes of an attributed string?

    The library offers a default conformance of CustomDumpRepresentable for NSAttributedString that only dumps the string value.

    https://github.com/pointfreeco/swift-custom-dump/blob/819d9d370cd721c9d87671e29d947279292e4541/Sources/CustomDump/Conformances/Foundation.swift#L77

    This removes all the attributes from the dump That information might be relevant but there's no way to print any additional information. Curious why this was decided.

    opened by tahirmt 0
  • Generic type is ignored

    Generic type is ignored

    Describe the bug When trying to dump a generic type, the generic information is not included in the dump

    To Reproduce

    func testGeneric() {
        struct Box<T> {
            let value: T
        }
    
        let stringBox = Box(value: "test")
    
        var dump = ""
        customDump(
            stringBox,
            to: &dump
        )
        XCTAssertNoDifference(
            dump,
        """
        DumpTests.Box(value: \"test\")
        """
        )
        // Expecting the dump to be DumpTests.Box<String>(value: \"test\") instead
    }
    

    Expected behavior Expecting the generic type information to be included in the dump i.e. DumpTests.Box<String>(value: \"test\")

    Environment

    • swift-custom-dump version [e.g. 0.6.0]
    • Xcode [e.g. 13.4.1]
    • Swift [e.g. 5.6]
    • OS: [e.g. macOS 12.6]
    opened by tahirmt 0
  • add exclude NoChanged Property option

    add exclude NoChanged Property option

    Hi, I find that Collection type automatic collapse unchanged value is pretty neat. Can we extend collapse to struct type?

    I tried some test cases, but it seems like some wired comma at the end of property diff. 😅

    opened by ytyubox 0
Releases(0.6.1)
  • 0.6.1(Nov 7, 2022)

    What's Changed

    • Fixed: KeyPath release dumps on Darwin platforms should no longer crash (https://github.com/pointfreeco/swift-custom-dump/pull/68).
    • Fixed: AnyHashable dump has been improved (thanks @tahirmt, https://github.com/pointfreeco/swift-custom-dump/pull/64).
    • Infrastructure: Added ObjectIdentifier dump test (thanks @tahirmt, https://github.com/pointfreeco/swift-custom-dump/pull/66).

    Full Changelog: https://github.com/pointfreeco/swift-custom-dump/compare/0.6.0...0.6.1

    Source code(tar.gz)
    Source code(zip)
  • 0.6.0(Oct 3, 2022)

    • Added: Objects that are printed more than once per dump are now identified, e.g. MyObject#1(…) instead of MyObject(…) (thanks @tahirmt).
    • Fixed: Superclass fields are now correctly dumped alongside fields defined on the subclass (thanks @tahirmt).
    Source code(tar.gz)
    Source code(zip)
  • 0.5.2(Sep 14, 2022)

  • 0.5.1(Sep 13, 2022)

  • 0.5.0(Jun 10, 2022)

    • Fixed: diffs now differentiate when value dumps the same output, but the type differs.

      let xs: [Any] = [Float(42)]
      let ys: [Any] = [Double(42)]
      diff(xs, ys)
      //  [
      // -  [0]: 42 as Float
      // +  [0]: 42 as Double
      //  ]
      
    Source code(tar.gz)
    Source code(zip)
  • 0.4.0(Apr 28, 2022)

    What's Changed

    • Update README.md by @iampatbrown in https://github.com/pointfreeco/swift-custom-dump/pull/34
    • Remove platform requirements by @stephencelis in https://github.com/pointfreeco/swift-custom-dump/pull/37
    • Run 13.2 CI on experimental build system by @stephencelis in https://github.com/pointfreeco/swift-custom-dump/pull/38
    • Add CI labels by @stephencelis in https://github.com/pointfreeco/swift-custom-dump/pull/39
    • Add issue templates and CoC by @stephencelis in https://github.com/pointfreeco/swift-custom-dump/pull/41
    • Revert removal of platform requirements by @stephencelis in https://github.com/pointfreeco/swift-custom-dump/pull/42
    • Show label for visited items by @brentleyjones in https://github.com/pointfreeco/swift-custom-dump/pull/43
    • fix: Resolve Xcode 13 project startup beachball due to this playground by @importRyan in https://github.com/pointfreeco/swift-custom-dump/pull/46

    New Contributors

    • @iampatbrown made their first contribution in https://github.com/pointfreeco/swift-custom-dump/pull/34
    • @brentleyjones made their first contribution in https://github.com/pointfreeco/swift-custom-dump/pull/43
    • @importRyan made their first contribution in https://github.com/pointfreeco/swift-custom-dump/pull/46

    Full Changelog: https://github.com/pointfreeco/swift-custom-dump/compare/0.3.0...0.3.1

    Source code(tar.gz)
    Source code(zip)
  • 0.3.0(Dec 8, 2021)

    • Fixed: differences that were previously undetected in the dump are now better surfaced, e.g. when object identity differs, it will be printed in the diff.
    • Fixed: Xcode 13.2's new experimental build system is now supported (thanks @jaanussiim).
    Source code(tar.gz)
    Source code(zip)
  • 0.2.1(Oct 13, 2021)

  • 0.2.0(Sep 22, 2021)

    • Updated: Improved dumping of errors bridged to NSError (thanks @aroben).
    • Updated: Inline types no longer include (unknown context) in their dumps.
    • Fixed: Swift 5.5 support on macCatalyst, Linux, and Windows (thanks @klundberg, @davdroman).
    • Infrastructure: CI improvements, including running on Windows and against more versions of Swift (thanks @Jeehut, @davdroman).
    Source code(tar.gz)
    Source code(zip)
  • 0.1.3(Sep 14, 2021)

    • Added: a bunch of new conformances (thanks @noppefoxwolf, @konomae, @Bellaposa).
    • Fixed: macOS builds on Big Sur using Xcode 13 RC no longer fail.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.2(Aug 23, 2021)

    • Added: more conformances for Photos, StoreKit, and UIKit (thanks @larryonoff).
    • Fixed: a build failure for watchOS in Xcode 13.
    • Infrastructure: continuous integration now run tests and builds for release on all Apple platforms over a number of recent Xcode versions.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.1(Aug 23, 2021)

    • Added: platform requirements. In order to get things building/archiving reliably we have added platform requirements targeting iOS 13, macOS 10.15, tvOS 13, and watchOS 6. We are open to loosening these requirements in the future but want Custom Dump to reliably build across supported versions of Xcode.
    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Aug 23, 2021)

Owner
Point-Free
A video series exploring Swift and functional programming.
Point-Free
Network debugging made easy,This network debugging tool is developed based on the swift version of Wormholy.

Start debugging iOS network calls like a wizard, without extra code! Wormholy makes debugging quick and reliable. What you can do: No code to write an

null 21 Dec 14, 2022
Set of easy to use debugging tools for iOS developers & QA engineers.

DBDebugToolkit DBDebugToolkit is a debugging library written in Objective-C. It is meant to provide as many easily accessible tools as possible while

Dariusz Bukowski 1.2k Dec 30, 2022
Profiling / Debugging assist tools for iOS. (Memory Leak, OOM, ANR, Hard Stalling, Network, OpenGL, Time Profile ...)

MTHawkeye Readme 中文版本 MTHawkeye is profiling, debugging tools for iOS used in Meitu. It's designed to help iOS developers improve development producti

meitu 1.4k Dec 29, 2022
Chisel is a collection of LLDB commands to assist debugging iOS apps.

Chisel Chisel is a collection of LLDB commands to assist in the debugging of iOS apps. [Installation • Commands • Custom Commands • Development Workfl

Facebook 8.9k Jan 6, 2023
An in-app debugging and exploration tool for iOS

FLEX FLEX (Flipboard Explorer) is a set of in-app debugging and exploration tools for iOS development. When presented, FLEX shows a toolbar that lives

FLEXTool 13.3k Dec 31, 2022
Dotzu In-App iOS Debugging Tool With Enhanced Logging, Networking Info, Crash reporting And More.

Dotzu In-App iOS Debugging Tool With Enhanced Logging, Networking Info, Crash reporting And More. The debugger tool for iOS developer. Display logs, n

Remi ROBERT 1.8k Jan 3, 2023
A lightweight, one line setup, iOS / OSX network debugging library! 🦊

Netfox provides a quick look on all executed network requests performed by your iOS or OSX app. It grabs all requests - of course yours, requests from

Christos Kasketis 3.4k Dec 28, 2022
Next generation debugging framework for iOS

Alpha is the idea of a next generation debugging framework for iOS applications. It combines multiple debugging tools built on top of a simple, unifie

Dal Rupnik 733 Oct 29, 2022
Droar is a modular, single-line installation debugging window

Droar is a modular, single-line installation debugging window. Overview The idea behind Droar is simple: during app deployment stages, adding quick ap

Myriad Mobile 55 Sep 24, 2022
iOS network debugging, like a wizard 🧙‍♂️

Start debugging iOS network calls like a wizard, without extra code! Wormholy makes debugging quick and reliable. What you can do: No code to write an

Paolo Musolino 2.1k Jan 8, 2023
Free macOS app for iOS view debugging.

Introduction You can inspect and modify views in iOS app via Lookin, just like UI Inspector in Xcode, or another app called Reveal. Official Website:h

Li Kai 575 Dec 28, 2022
Convenient debugging button.

FunnyButton Example 在平时开发,运行期间有时候想中途看一下某个视图或变量的信息,虽说打断点是可以查看,但有时候断点调试有时候会卡住好一会才能看到(尤其是大项目经常卡很久),极度影响效率。 基于这种情况,FunnyButton就是为了能够便捷调试的全局按钮,添加好点击事件,就能随时

健了个平_(:з」∠)_ 2 Sep 20, 2022
In-app console and debug tools for iOS developers

LocalConsole Welcome to LocalConsole! This Swift Package makes on-device debugging easy with a convenient PiP-style console that can display items in

Duraid Abdul 650 Jan 1, 2023
Customizable Console UI overlay with debug log on top of your iOS App

AEConsole Customizable Console UI overlay with debug log on top of your iOS App AEConsole is built on top of AELog, so you should probably see that fi

Marko Tadić 142 Dec 21, 2022
TouchInspector - a lightweight package that helps you visualize and debug touches on iOS and iPadOS

TouchInspector is a lightweight package that helps you visualize and debug touches on iOS and iPadOS.

Janum Trivedi 116 Jan 3, 2023
Automaticly display Log,Crash,Network,ANR,Leak,CPU,RAM,FPS,NetFlow,Folder and etc with one line of code based on Swift. Just like God opened his eyes

GodEye Automaticly display Log,Crash,Network,ANR,Leak,CPU,RAM,FPS,NetFlow,Folder and etc with one line of code based on Swift. Just like God opened hi

陈奕龙(子循) 3.7k Dec 23, 2022
In-app design review tool to inspect measurements, attributes, and animations.

Hyperion Hyperion - In App Design Review Tool What is it? Hyperion is a hidden plugin drawer that can easily be integrated into any app. The drawer si

WillowTree, LLC 2k Dec 27, 2022
Tool to debug layouts directly on iOS devices: inspect layers in 3D and debug each visible view attributes

Introduction Features Inspect layouts directly on iOS devices Inspection could be triggered only if app is running under DEBUG build configuration, so

Ihor Savynskyi 510 Dec 24, 2022
📘A library for isolated developing UI components and automatically taking snapshots of them.

A library for isolated developing UI components and automatically taking snapshots of them. Playbook Playbook is a library that provides a sandbox for

Playbook 970 Jan 3, 2023