├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── Assets └── combine-printout.png ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── CombinePrintout │ └── CombinePrintout.swift └── Tests ├── CombinePrintoutTests ├── CombinePrintoutTests.swift └── XCTestManifests.swift └── LinuxMain.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots/**/*.png 68 | fastlane/test_output 69 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Assets/combine-printout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CombineCommunity/CombinePrintout/721fd799f083c57ca59f8658d874c854b2f97348/Assets/combine-printout.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Combine Open Source 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "CombinePrintout", 7 | platforms: [ 8 | .iOS(.v13), 9 | ], 10 | products: [ 11 | .library( 12 | name: "CombinePrintout", 13 | targets: ["CombinePrintout"]), 14 | ], 15 | targets: [ 16 | .target( 17 | name: "CombinePrintout", 18 | dependencies: []), 19 | .testTarget( 20 | name: "CombinePrintoutTests", 21 | dependencies: ["CombinePrintout"]), 22 | ] 23 | ) 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Combine Printout Logo](https://github.com/combineopensource/CombinePrintout/raw/master/Assets/combine-printout.png) 2 | 3 | A Combine micro debugging framework. It helps you log subscription events to the console so you can track their life cycle. 4 | 5 | ## Usage 6 | 7 | #### A debugging print sink 8 | 9 | The built-in `print()` operator is useful but you still need to add a subscriber to your subscription. `printSink()` is a subscriber that you can use to debug a subscription without the need to add a separate subscriber like so: 10 | 11 | ```swift 12 | Just(["One", "Two"]) 13 | .printSink() 14 | ``` 15 | 16 | `printSink()` will subscribe the publisher and log all events like so: 17 | 18 | ```none 19 | Sink: output(["One", "Two"]) 20 | Sink: finished 21 | ``` 22 | 23 | #### A debugging print cancellable 24 | 25 | If you're building more complex memory management logic or not sure when are your cancellables released you can use `printCancellable()` to log a given `Cancellable`'s life cycle like so: 26 | 27 | ```swift 28 | Just(["One", "Two"]) 29 | .assign(to: \.model, on: self) 30 | .printCancellable() 31 | .store(in: &subscriptions) 32 | ``` 33 | 34 | `printCancellable()` wraps the `AnyCancellable` returned from `assign(to:on:)` and logs all the received events: 35 | 36 | ```none 37 | Cancellable: init 38 | ... 39 | (self.subscriptions is released from memory) 40 | ... 41 | Cancellable: cancel 42 | Cancellable: deinit 43 | ``` 44 | 45 | ## Installation 46 | 47 | ### Swift Package Manager 48 | 49 | Add the following dependency to your **Package.swift** file: 50 | 51 | ```swift 52 | .package(url: "https://github.com/combineopensource/CombinePrintout, from: "0.2") 53 | ``` 54 | ## License 55 | 56 | CombineOpenSource is available under the MIT license. See the LICENSE file for more info. 57 | 58 | 59 | ## Credit 60 | 61 | Copyright (c) 2019 Combine Open Source 62 | 63 | Created by: Marin Todorov 64 | -------------------------------------------------------------------------------- /Sources/CombinePrintout/CombinePrintout.swift: -------------------------------------------------------------------------------- 1 | import Combine 2 | 3 | class PrintCancellable: Cancellable { 4 | let wrapped: AnyCancellable 5 | let id: String 6 | 7 | init(_ wrapped: AnyCancellable, id: String? = nil) { 8 | self.wrapped = wrapped 9 | self.id = id ?? "" 10 | Swift.print("Cancellable: init \(self.id)") 11 | } 12 | 13 | func cancel() { 14 | wrapped.cancel() 15 | Swift.print("Cancellable: cancel \(id)") 16 | } 17 | 18 | deinit { 19 | Swift.print("Cancellable: deinit \(id)") 20 | } 21 | } 22 | 23 | extension Cancellable { 24 | 25 | /// A debugging `Cancellable` wrapper that prints out `Cancellable` life cycle events. 26 | /// - Parameter id: An optional identifier to print along the output. 27 | public func printCancellable(id: String? = nil) -> AnyCancellable { 28 | return AnyCancellable(PrintCancellable(AnyCancellable(self), id: id)) 29 | } 30 | } 31 | 32 | extension Publisher { 33 | 34 | /// A debugging sink that prints all received events. 35 | /// - Parameter id: An optional identifier to print along the output. 36 | public func printSink(id: String = "") -> AnyCancellable { 37 | return sink(receiveCompletion: { Swift.print("Sink: \($0) \(id)") }, 38 | receiveValue: { Swift.print("Sink: output(\($0)) \(id)") }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Tests/CombinePrintoutTests/CombinePrintoutTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import CombinePrintout 3 | 4 | final class CombinePrintoutTests: XCTestCase { 5 | func testExample() { 6 | // This is an example of a functional test case. 7 | // Use XCTAssert and related functions to verify your tests produce the correct 8 | // results. 9 | XCTAssertEqual(CombinePrintout().text, "Hello, World!") 10 | } 11 | 12 | static var allTests = [ 13 | ("testExample", testExample), 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /Tests/CombinePrintoutTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(CombinePrintoutTests.allTests), 7 | ] 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import CombinePrintoutTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += CombinePrintoutTests.allTests() 7 | XCTMain(tests) 8 | --------------------------------------------------------------------------------