├── .gitignore ├── .swiftlint.yml ├── App ├── .swiftlint.yml ├── CombineOperators.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── CombineOperators │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon1024.png │ │ │ ├── icon_120-1.png │ │ │ ├── icon_120.png │ │ │ ├── icon_152.png │ │ │ ├── icon_167.png │ │ │ ├── icon_180.png │ │ │ ├── icon_20.png │ │ │ ├── icon_29.png │ │ │ ├── icon_40-1.png │ │ │ ├── icon_40-2.png │ │ │ ├── icon_40.png │ │ │ ├── icon_58-1.png │ │ │ ├── icon_58.png │ │ │ ├── icon_60.png │ │ │ ├── icon_76.png │ │ │ ├── icon_80-1.png │ │ │ ├── icon_80.png │ │ │ └── icon_87.png │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Combine Ops.entitlements │ ├── Info.plist │ └── SceneDelegate.swift ├── CombineOperatorsTests │ ├── CombineOperatorsTests.swift │ └── Info.plist └── CombineOperatorsUITests │ ├── CombineOperatorsUITests.swift │ └── Info.plist ├── Docs ├── interactivecombine.gif └── promo.png ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── CombineOperatorsCore │ ├── CombineOperatorsCore.swift │ ├── Models │ ├── Operator.swift │ ├── OperatorVariant.swift │ └── Subject.swift │ ├── OperatorScene │ ├── Nodes │ │ ├── BandNode.swift │ │ ├── OldValueNode.swift │ │ ├── OperatorNode.swift │ │ ├── PublisherNode.swift │ │ ├── SubjectNode.swift │ │ ├── TimerNode.swift │ │ └── ValueNode.swift │ ├── OperatorScene.swift │ └── TimeUpdatingScene.swift │ ├── Resources │ ├── Media.xcassets │ │ ├── Color │ │ │ ├── Contents.json │ │ │ ├── countdownFill.colorset │ │ │ │ └── Contents.json │ │ │ ├── countdownTrack.colorset │ │ │ │ └── Contents.json │ │ │ ├── errorButton.colorset │ │ │ │ └── Contents.json │ │ │ ├── errorButtonHighlighted.colorset │ │ │ │ └── Contents.json │ │ │ ├── finishButton.colorset │ │ │ │ └── Contents.json │ │ │ ├── finishButtonHighlighted.colorset │ │ │ │ └── Contents.json │ │ │ ├── inputButton.colorset │ │ │ │ └── Contents.json │ │ │ ├── inputButtonHighlighted.colorset │ │ │ │ └── Contents.json │ │ │ ├── macNavBarTintColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── operatorFinished.colorset │ │ │ │ └── Contents.json │ │ │ ├── operatorFinishedError.colorset │ │ │ │ └── Contents.json │ │ │ ├── operatorOldValue.colorset │ │ │ │ └── Contents.json │ │ │ ├── operatorValue.colorset │ │ │ │ └── Contents.json │ │ │ ├── subjectOldValue.colorset │ │ │ │ └── Contents.json │ │ │ └── subjectValue.colorset │ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── Shared │ │ │ ├── Contents.json │ │ │ ├── band_operator.imageset │ │ │ ├── Contents.json │ │ │ ├── band_operator.png │ │ │ ├── band_operator@2x.png │ │ │ └── band_operator@3x.png │ │ │ ├── band_subject.imageset │ │ │ ├── Contents.json │ │ │ ├── band_subject.png │ │ │ ├── band_subject@2x.png │ │ │ └── band_subject@3x.png │ │ │ ├── number_of_subscriptions.imageset │ │ │ ├── Contents.json │ │ │ └── number_of_subscriptions.pdf │ │ │ ├── operator.imageset │ │ │ ├── Contents.json │ │ │ ├── operator.png │ │ │ ├── operator@2x.png │ │ │ └── operator@3x.png │ │ │ ├── pipe_left.imageset │ │ │ ├── Contents.json │ │ │ ├── pipe_left.png │ │ │ ├── pipe_left@2x.png │ │ │ └── pipe_left@3x.png │ │ │ ├── pipe_right.imageset │ │ │ ├── Contents.json │ │ │ ├── pipe_right.png │ │ │ ├── pipe_right@2x.png │ │ │ └── pipe_right@3x.png │ │ │ ├── subject.imageset │ │ │ ├── Contents.json │ │ │ ├── subject.png │ │ │ ├── subject@2x.png │ │ │ └── subject@3x.png │ │ │ ├── value_operator.imageset │ │ │ ├── Contents.json │ │ │ ├── value_operator.png │ │ │ ├── value_operator@2x.png │ │ │ └── value_operator@3x.png │ │ │ ├── value_operator_old.imageset │ │ │ ├── Contents.json │ │ │ ├── value_operator_old.png │ │ │ ├── value_operator_old@2x.png │ │ │ └── value_operator_old@3x.png │ │ │ ├── value_subject.imageset │ │ │ ├── Contents.json │ │ │ ├── value_subject.png │ │ │ ├── value_subject@2x.png │ │ │ └── value_subject@3x.png │ │ │ └── value_subject_old.imageset │ │ │ ├── Contents.json │ │ │ ├── value_subject_old.png │ │ │ ├── value_subject_old@2x.png │ │ │ └── value_subject_old@3x.png │ └── SwiftGen │ │ └── Assets.swift │ ├── ViewControllers │ ├── BaseOperatorViewController.swift │ ├── OperatorInfoViewController.swift │ ├── OperatorViewControllers │ │ ├── Collecting │ │ │ ├── OperatorAllSatisfyViewController.swift │ │ │ ├── OperatorContainsViewController.swift │ │ │ ├── OperatorCountViewController.swift │ │ │ ├── OperatorMaxViewController.swift │ │ │ ├── OperatorMinViewController.swift │ │ │ ├── OperatorOutputAtViewController.swift │ │ │ └── OperatorReduceViewController.swift │ │ ├── Combining │ │ │ ├── OperatorAppendViewController.swift │ │ │ ├── OperatorCombineLatestViewController.swift │ │ │ ├── OperatorDropUntilOutputFromViewController.swift │ │ │ ├── OperatorFlatMapViewController.swift │ │ │ ├── OperatorMapAndSwitchToLatestViewController.swift │ │ │ ├── OperatorMergeViewController.swift │ │ │ ├── OperatorPrefixUntilOutputFromViewController.swift │ │ │ ├── OperatorPrependViewController.swift │ │ │ └── OperatorZipViewController.swift │ │ ├── Error handling │ │ │ ├── OperatorCatchErrorViewController.swift │ │ │ ├── OperatorMapErrorViewController.swift │ │ │ └── OperatorReplaceErrorViewController.swift │ │ ├── Filtering │ │ │ ├── OperatorCompactMapViewController.swift │ │ │ ├── OperatorDropFirstViewController.swift │ │ │ ├── OperatorDropWhileViewController.swift │ │ │ ├── OperatorFilterViewController.swift │ │ │ ├── OperatorFirstViewController.swift │ │ │ ├── OperatorIgnoreOutputViewController.swift │ │ │ ├── OperatorLastViewController.swift │ │ │ ├── OperatorPrefixViewController.swift │ │ │ └── OperatorRemoveDuplicatesViewController.swift │ │ ├── Timing │ │ │ ├── OperatorCollectByTimeViewController.swift │ │ │ ├── OperatorDebounceViewController.swift │ │ │ ├── OperatorDelayViewController.swift │ │ │ ├── OperatorMeasureIntervalViewController.swift │ │ │ ├── OperatorThrottleViewController.swift │ │ │ └── OperatorTimeoutViewController.swift │ │ └── Transforming │ │ │ ├── OperatorCollectViewController.swift │ │ │ ├── OperatorMapViewController.swift │ │ │ ├── OperatorReplaceEmptyViewController.swift │ │ │ ├── OperatorReplaceNilViewController.swift │ │ │ └── OperatorScanViewController.swift │ ├── OperatorsTableViewController.swift │ └── SymbolsTableViewController.swift │ └── Views │ ├── InputButton.swift │ └── SubjectView.swift ├── Tests ├── CombineOperatorsCoreTests │ ├── CombineOperatorsCoreTests.swift │ └── XCTestManifests.swift └── LinuxMain.swift └── swiftgen.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - trailing_whitespace 3 | - unused_setter_value 4 | - nesting 5 | - opening_brace 6 | 7 | opt_in_rules: 8 | - force_unwrapping 9 | - unavailable_function 10 | 11 | line_length: 200 12 | 13 | function_parameter_count: 8 14 | 15 | file_length: 500 16 | 17 | identifier_name: 18 | min_length: 1 19 | max_length: 50 20 | 21 | type_name: 22 | max_length: 23 | warning: 80 24 | error: 100 25 | 26 | type_body_length: 27 | warning: 400 28 | error: 600 29 | -------------------------------------------------------------------------------- /App/.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - trailing_whitespace 3 | - unused_setter_value 4 | - nesting 5 | - opening_brace 6 | 7 | opt_in_rules: 8 | - force_unwrapping 9 | - unavailable_function 10 | 11 | line_length: 200 12 | 13 | function_parameter_count: 8 14 | 15 | file_length: 500 16 | 17 | identifier_name: 18 | min_length: 1 19 | max_length: 50 20 | 21 | type_name: 22 | max_length: 23 | warning: 80 24 | error: 100 25 | 26 | type_body_length: 27 | warning: 400 28 | error: 600 29 | -------------------------------------------------------------------------------- /App/CombineOperators.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /App/CombineOperators.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /App/CombineOperators/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // CombineOperators 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | 28 | @UIApplicationMain 29 | class AppDelegate: UIResponder, UIApplicationDelegate { 30 | 31 | func application( 32 | _ application: UIApplication, 33 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 34 | ) -> Bool { 35 | // Override point for customization after application launch. 36 | return true 37 | } 38 | 39 | // MARK: UISceneSession Lifecycle 40 | 41 | func application( 42 | _ application: UIApplication, 43 | configurationForConnecting connectingSceneSession: UISceneSession, 44 | options: UIScene.ConnectionOptions 45 | ) -> UISceneConfiguration { 46 | // Called when a new scene session is being created. 47 | // Use this method to select a configuration to create the new scene with. 48 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "icon_40.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "icon_60.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "icon_58.png", 17 | "idiom" : "iphone", 18 | "scale" : "2x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "icon_87.png", 23 | "idiom" : "iphone", 24 | "scale" : "3x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "icon_80.png", 29 | "idiom" : "iphone", 30 | "scale" : "2x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "filename" : "icon_120.png", 35 | "idiom" : "iphone", 36 | "scale" : "3x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "icon_120-1.png", 41 | "idiom" : "iphone", 42 | "scale" : "2x", 43 | "size" : "60x60" 44 | }, 45 | { 46 | "filename" : "icon_180.png", 47 | "idiom" : "iphone", 48 | "scale" : "3x", 49 | "size" : "60x60" 50 | }, 51 | { 52 | "filename" : "icon_20.png", 53 | "idiom" : "ipad", 54 | "scale" : "1x", 55 | "size" : "20x20" 56 | }, 57 | { 58 | "filename" : "icon_40-1.png", 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "20x20" 62 | }, 63 | { 64 | "filename" : "icon_29.png", 65 | "idiom" : "ipad", 66 | "scale" : "1x", 67 | "size" : "29x29" 68 | }, 69 | { 70 | "filename" : "icon_58-1.png", 71 | "idiom" : "ipad", 72 | "scale" : "2x", 73 | "size" : "29x29" 74 | }, 75 | { 76 | "filename" : "icon_40-2.png", 77 | "idiom" : "ipad", 78 | "scale" : "1x", 79 | "size" : "40x40" 80 | }, 81 | { 82 | "filename" : "icon_80-1.png", 83 | "idiom" : "ipad", 84 | "scale" : "2x", 85 | "size" : "40x40" 86 | }, 87 | { 88 | "filename" : "icon_76.png", 89 | "idiom" : "ipad", 90 | "scale" : "1x", 91 | "size" : "76x76" 92 | }, 93 | { 94 | "filename" : "icon_152.png", 95 | "idiom" : "ipad", 96 | "scale" : "2x", 97 | "size" : "76x76" 98 | }, 99 | { 100 | "filename" : "icon_167.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "83.5x83.5" 104 | }, 105 | { 106 | "filename" : "icon1024.png", 107 | "idiom" : "ios-marketing", 108 | "scale" : "1x", 109 | "size" : "1024x1024" 110 | } 111 | ], 112 | "info" : { 113 | "author" : "xcode", 114 | "version" : 1 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon1024.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_120-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_120-1.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_120.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_152.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_167.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_180.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_20.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_29.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_40-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_40-1.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_40-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_40-2.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_40.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_58-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_58-1.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_58.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_60.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_76.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_80-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_80-1.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_80.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/App/CombineOperators/Assets.xcassets/AppIcon.appiconset/icon_87.png -------------------------------------------------------------------------------- /App/CombineOperators/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /App/CombineOperators/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /App/CombineOperators/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /App/CombineOperators/Combine Ops.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /App/CombineOperators/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | 1 21 | LSApplicationCategoryType 22 | public.app-category.developer-tools 23 | LSRequiresIPhoneOS 24 | 25 | UIApplicationSceneManifest 26 | 27 | UIApplicationSupportsMultipleScenes 28 | 29 | UISceneConfigurations 30 | 31 | UIWindowSceneSessionRoleApplication 32 | 33 | 34 | UISceneConfigurationName 35 | Default Configuration 36 | UISceneDelegateClassName 37 | $(PRODUCT_MODULE_NAME).SceneDelegate 38 | UISceneStoryboardFile 39 | Main 40 | 41 | 42 | 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /App/CombineOperators/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // CombineOperators 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import CombineOperatorsCore 28 | 29 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 30 | 31 | var window: UIWindow? 32 | 33 | func scene( 34 | _ scene: UIScene, 35 | willConnectTo session: UISceneSession, 36 | options connectionOptions: UIScene.ConnectionOptions 37 | ) { 38 | 39 | #if targetEnvironment(macCatalyst) 40 | 41 | guard let windowScene = (scene as? UIWindowScene) else { return } 42 | 43 | UINavigationBar.appearance().tintColor = Assets.macNavBarTintColor.color 44 | 45 | windowScene.title = "Combine Operators" 46 | 47 | let splitViewController = UISplitViewController() 48 | splitViewController.preferredDisplayMode = .allVisible 49 | splitViewController.presentsWithGesture = false 50 | splitViewController.primaryBackgroundStyle = .sidebar 51 | 52 | let navigationController = UINavigationController(rootViewController: OperatorsTableViewController(isCompact: false)) 53 | 54 | splitViewController.viewControllers = [navigationController] 55 | 56 | window?.rootViewController = splitViewController 57 | 58 | #else 59 | 60 | let splitViewController = UISplitViewController(style: .doubleColumn) 61 | splitViewController.preferredDisplayMode = .twoDisplaceSecondary 62 | splitViewController.presentsWithGesture = false 63 | splitViewController.preferredSplitBehavior = .tile 64 | 65 | let operatorsTableViewController = OperatorsTableViewController(isCompact: false) 66 | 67 | splitViewController.setViewController(operatorsTableViewController, for: .primary) 68 | 69 | let compactOperatorsTableViewController = OperatorsTableViewController(isCompact: true) 70 | let navigationController = UINavigationController(rootViewController: compactOperatorsTableViewController) 71 | splitViewController.setViewController(navigationController, for: .compact) 72 | 73 | window?.rootViewController = splitViewController 74 | 75 | #endif 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /App/CombineOperatorsTests/CombineOperatorsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CombineOperatorsTests.swift 3 | // CombineOperatorsTests 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import XCTest 27 | @testable import CombineOperators 28 | 29 | class CombineOperatorsTests: XCTestCase { 30 | 31 | override func setUpWithError() throws { 32 | // Put setup code here. This method is called before the invocation of each test method in the class. 33 | } 34 | 35 | override func tearDownWithError() throws { 36 | // Put teardown code here. This method is called after the invocation of each test method in the class. 37 | } 38 | 39 | func testExample() throws { 40 | // This is an example of a functional test case. 41 | // Use XCTAssert and related functions to verify your tests produce the correct results. 42 | } 43 | 44 | func testPerformanceExample() throws { 45 | // This is an example of a performance test case. 46 | self.measure { 47 | // Put the code you want to measure the time of here. 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /App/CombineOperatorsTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /App/CombineOperatorsUITests/CombineOperatorsUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CombineOperatorsUITests.swift 3 | // CombineOperatorsUITests 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import XCTest 27 | 28 | class CombineOperatorsUITests: XCTestCase { 29 | 30 | override func setUpWithError() throws { 31 | // Put setup code here. This method is called before the invocation of each test method in the class. 32 | 33 | // In UI tests it is usually best to stop immediately when a failure occurs. 34 | continueAfterFailure = false 35 | } 36 | 37 | override func tearDownWithError() throws { 38 | // Put teardown code here. This method is called after the invocation of each test method in the class. 39 | } 40 | 41 | func testExample() throws { 42 | // UI tests must launch the application that they test. 43 | let app = XCUIApplication() 44 | app.launch() 45 | 46 | // Use recording to get started writing UI tests. 47 | // Use XCTAssert and related functions to verify your tests produce the correct results. 48 | } 49 | 50 | func testLaunchPerformance() throws { 51 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { 52 | // This measures how long it takes to launch your application. 53 | measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) { 54 | XCUIApplication().launch() 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /App/CombineOperatorsUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Docs/interactivecombine.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Docs/interactivecombine.gif -------------------------------------------------------------------------------- /Docs/promo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Docs/promo.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 cocoatoucher 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.3 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "CombineOperatorsCore", 8 | platforms: [ 9 | .iOS(.v14) 10 | ], 11 | products: [ 12 | .library(name: "CombineOperatorsCore", targets: ["CombineOperatorsCore"]) 13 | ], 14 | dependencies: [], 15 | targets: [ 16 | .target(name: "CombineOperatorsCore"), 17 | .testTarget(name: "CombineOperatorsCoreTests", dependencies: ["CombineOperatorsCore"]) 18 | ], 19 | swiftLanguageVersions: [.v5] 20 | ) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Combine Operators App Store 4 |

5 | 6 | # Combine Operators 7 | 8 |

9 | 10 |

11 |

12 | 13 | 14 | 15 | 16 |

17 | 18 | Combine Operators is an interactive tutorial/cheat sheet app that you can download on the iOS App Store. It lets you play with Combine framework publishers and can help you grasp basic concepts if you are a beginner or refresh your memory during your further journey with the framework. 19 | 20 | With Combine Operators you can 21 | - Interact directly with underlying publisher instances through an intuitive UI. 22 | - Observe how subjects and operators behave with different input sequences through helpful animations. 23 | 24 | There are plans to make Combine Operators support more advanced input values and subject/operator combinations. Stay tuned and contribute! 🙌 25 | 26 | *The app is not implemented using SwiftUI, but that is a future plan as well. 27 | 28 |

29 | 30 | A glimpse of the app's interactive features 31 | 32 |

33 | 34 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/CombineOperatorsCore.swift: -------------------------------------------------------------------------------- 1 | struct CombineOperatorsCore { 2 | var text = "Hello, World!" 3 | } 4 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Models/Operator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Operator.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import Foundation 27 | 28 | struct Operator { 29 | let name: String 30 | let description: String? 31 | } 32 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Models/Subject.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Subject.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import Foundation 27 | import Combine 28 | 29 | enum PublisherUpdate { 30 | case subscription 31 | case output(value: String?) 32 | case completion(isError: Bool) 33 | case cancel 34 | case request 35 | case subscriptionCount(Int) 36 | } 37 | 38 | class Subject { 39 | let title: String 40 | let publisher: PassthroughSubject 41 | let inputValues: [String?] 42 | 43 | var trackedPublisher: AnyPublisher? 44 | 45 | var isFinished: Bool = false 46 | 47 | var handleUpdate: ((PublisherUpdate) -> Void)? 48 | 49 | var numberOfSubcriptions: Int = 0 { 50 | didSet { 51 | handleUpdate?(.subscriptionCount(numberOfSubcriptions)) 52 | } 53 | } 54 | 55 | internal init( 56 | title: String, 57 | inputValues: [String?]) { 58 | self.title = title 59 | let publisher = PassthroughSubject() 60 | self.publisher = publisher 61 | self.inputValues = inputValues 62 | 63 | self.trackedPublisher = publisher.handleEvents( 64 | receiveSubscription: { [weak self] _ in 65 | guard let self = self else { return } 66 | self.numberOfSubcriptions += 1 67 | self.handleUpdate?(.subscription) 68 | }, receiveOutput: { [weak self] value in 69 | guard let self = self else { return } 70 | self.handleUpdate?(.output(value: value)) 71 | }, receiveCompletion: { [weak self] completion in 72 | guard let self = self else { return } 73 | self.numberOfSubcriptions = 0 74 | switch completion { 75 | case .failure: 76 | self.handleUpdate?(.completion(isError: true)) 77 | case .finished: 78 | self.handleUpdate?(.completion(isError: false)) 79 | } 80 | }, receiveCancel: { [weak self] in 81 | guard let self = self else { return } 82 | self.numberOfSubcriptions = 0 83 | self.handleUpdate?(.cancel) 84 | }, receiveRequest: { [weak self] _ in 85 | guard let self = self else { return } 86 | self.handleUpdate?(.request) 87 | } 88 | ) 89 | .eraseToAnyPublisher() 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/OperatorScene/Nodes/BandNode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BandNode.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import SpriteKit 27 | 28 | class BandNode: SKSpriteNode { 29 | 30 | init(isOperator: Bool = false) { 31 | super.init(texture: nil, color: .gray, size: CGSize(width: 32, height: 200)) 32 | 33 | name = "band" 34 | 35 | if isOperator { 36 | texture = SKTexture(image: Assets.bandOperator.image) 37 | } else { 38 | texture = SKTexture(image: Assets.bandSubject.image) 39 | } 40 | } 41 | 42 | @available(*, unavailable) 43 | required init?(coder aDecoder: NSCoder) { 44 | fatalError("init(coder:) has not been implemented") 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/OperatorScene/Nodes/OldValueNode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OldValueNode.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import SpriteKit 27 | 28 | class OldValueNode: SKSpriteNode { 29 | 30 | let value: String? 31 | 32 | init(value: String?, isOperator: Bool = false) { 33 | self.value = value 34 | let color = isOperator ? Assets.operatorOldValue.color : Assets.subjectOldValue.color 35 | super.init(texture: nil, color: color, size: CGSize(width: 32, height: 24)) 36 | 37 | name = "oldValue" 38 | 39 | setupNodes() 40 | } 41 | 42 | @available(*, unavailable) 43 | required init?(coder aDecoder: NSCoder) { 44 | fatalError("init(coder:) has not been implemented") 45 | } 46 | 47 | // MARK: - Private 48 | 49 | private lazy var labelNode: SKLabelNode = { 50 | let node = SKLabelNode() 51 | if value?.hasPrefix("finished-err") == true { 52 | if value?.hasSuffix("x") == true { 53 | node.text = "☠️ error" 54 | } else { 55 | node.text = "error" 56 | } 57 | } else { 58 | node.text = value 59 | } 60 | node.fontSize = 12.0 61 | node.fontName = UIFont.boldSystemFont(ofSize: 12.0).familyName 62 | node.fontColor = UIColor.white 63 | node.horizontalAlignmentMode = .center 64 | node.verticalAlignmentMode = .center 65 | return node 66 | }() 67 | 68 | private func setupNodes() { 69 | if value?.hasPrefix("finished") == true { 70 | if value?.hasSuffix("err") == true || value?.hasSuffix("err-x") == true { 71 | color = Assets.operatorFinishedError.color 72 | } else { 73 | color = Assets.operatorFinished.color 74 | } 75 | } 76 | 77 | addChild(labelNode) 78 | 79 | let labelWidth = labelNode.calculateAccumulatedFrame().size.width 80 | let width = max(labelWidth + 10, 32) 81 | size.width = width 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/OperatorScene/Nodes/ValueNode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ValueNode.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import SpriteKit 27 | 28 | class ValueNode: SKSpriteNode { 29 | 30 | let value: String 31 | var isRemoved: Bool = false 32 | 33 | init(value: String, isOperator: Bool = false) { 34 | self.value = value 35 | let color = isOperator ? Assets.operatorValue.color : Assets.subjectValue.color 36 | super.init(texture: nil, color: color, size: CGSize(width: 50, height: 50)) 37 | 38 | name = "value" 39 | 40 | setupNodes() 41 | } 42 | 43 | @available(*, unavailable) 44 | required init?(coder aDecoder: NSCoder) { 45 | fatalError("init(coder:) has not been implemented") 46 | } 47 | 48 | // MARK: - Private 49 | 50 | private lazy var labelNode: SKLabelNode = { 51 | let node = SKLabelNode() 52 | node.fontSize = 16.0 53 | node.fontName = UIFont.boldSystemFont(ofSize: 16.0).familyName 54 | node.fontColor = UIColor.white 55 | node.horizontalAlignmentMode = .center 56 | node.verticalAlignmentMode = .center 57 | return node 58 | }() 59 | 60 | private func setupNodes() { 61 | if value.hasPrefix("finished") == true { 62 | if value.hasSuffix("err") || value.hasSuffix("err-x") { 63 | color = Assets.operatorFinishedError.color 64 | } else { 65 | color = Assets.operatorFinished.color 66 | } 67 | } 68 | 69 | addChild(labelNode) 70 | 71 | if value.hasPrefix("finished-err") == true { 72 | if value.hasSuffix("x") == true { 73 | labelNode.text = "☠️ error" 74 | } else { 75 | labelNode.text = "error" 76 | } 77 | } else { 78 | labelNode.text = value 79 | } 80 | 81 | let labelWidth = labelNode.calculateAccumulatedFrame().size.width 82 | let width = max(labelWidth + 10, 50) 83 | size.width = width 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/OperatorScene/TimeUpdatingScene.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimeUpdatingScene.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import SpriteKit 27 | 28 | class TimeUpdatingScene: SKScene { 29 | 30 | private(set) var currentTime: TimeInterval = 0 31 | private(set) var lastUpdateTime: TimeInterval = 0 32 | private(set) var lastDelta: TimeInterval = 0 33 | 34 | override func update(_ currentTime: TimeInterval) { 35 | self.currentTime = currentTime 36 | if lastUpdateTime == 0 { 37 | lastUpdateTime = currentTime 38 | } 39 | lastDelta = currentTime - lastUpdateTime 40 | } 41 | 42 | override func didFinishUpdate() { 43 | lastUpdateTime = currentTime 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/countdownFill.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.894", 9 | "green" : "0.475", 10 | "red" : "0.027" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/countdownTrack.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.776", 9 | "green" : "0.953", 10 | "red" : "0.937" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/errorButton.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x33", 9 | "green" : "0x56", 10 | "red" : "0xFF" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/errorButtonHighlighted.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.000", 9 | "green" : "0.184", 10 | "red" : "1.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/finishButton.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x8A", 9 | "green" : "0xC9", 10 | "red" : "0x24" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/finishButtonHighlighted.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x6B", 9 | "green" : "0x9C", 10 | "red" : "0x1C" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/inputButton.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.894", 9 | "green" : "0.659", 10 | "red" : "0.937" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/inputButtonHighlighted.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.925", 9 | "green" : "0.502", 10 | "red" : "1.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/macNavBarTintColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.449", 9 | "green" : "0.449", 10 | "red" : "0.449" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "1.000", 27 | "green" : "1.000", 28 | "red" : "1.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/operatorFinished.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x8A", 9 | "green" : "0xC9", 10 | "red" : "0x24" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/operatorFinishedError.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x33", 9 | "green" : "0x56", 10 | "red" : "0xFF" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/operatorOldValue.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.188", 9 | "green" : "0.145", 10 | "red" : "0.173" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/operatorValue.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.392", 9 | "green" : "0.220", 10 | "red" : "0.329" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/subjectOldValue.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.588", 9 | "green" : "0.486", 10 | "red" : "0.373" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Color/subjectValue.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.894", 9 | "green" : "0.475", 10 | "red" : "0.027" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/band_operator.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "band_operator.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "band_operator@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "band_operator@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/band_operator.imageset/band_operator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/band_operator.imageset/band_operator.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/band_operator.imageset/band_operator@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/band_operator.imageset/band_operator@2x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/band_operator.imageset/band_operator@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/band_operator.imageset/band_operator@3x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/band_subject.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "band_subject.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "band_subject@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "band_subject@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/band_subject.imageset/band_subject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/band_subject.imageset/band_subject.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/band_subject.imageset/band_subject@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/band_subject.imageset/band_subject@2x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/band_subject.imageset/band_subject@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/band_subject.imageset/band_subject@3x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/number_of_subscriptions.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "number_of_subscriptions.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/number_of_subscriptions.imageset/number_of_subscriptions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/number_of_subscriptions.imageset/number_of_subscriptions.pdf -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/operator.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "operator.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "operator@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "operator@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/operator.imageset/operator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/operator.imageset/operator.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/operator.imageset/operator@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/operator.imageset/operator@2x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/operator.imageset/operator@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/operator.imageset/operator@3x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/pipe_left.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pipe_left.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "pipe_left@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "pipe_left@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/pipe_left.imageset/pipe_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/pipe_left.imageset/pipe_left.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/pipe_left.imageset/pipe_left@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/pipe_left.imageset/pipe_left@2x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/pipe_left.imageset/pipe_left@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/pipe_left.imageset/pipe_left@3x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/pipe_right.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pipe_right.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "pipe_right@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "pipe_right@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/pipe_right.imageset/pipe_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/pipe_right.imageset/pipe_right.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/pipe_right.imageset/pipe_right@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/pipe_right.imageset/pipe_right@2x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/pipe_right.imageset/pipe_right@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/pipe_right.imageset/pipe_right@3x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/subject.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "subject.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "subject@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "subject@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/subject.imageset/subject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/subject.imageset/subject.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/subject.imageset/subject@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/subject.imageset/subject@2x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/subject.imageset/subject@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/subject.imageset/subject@3x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_operator.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "value_operator.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "value_operator@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "value_operator@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_operator.imageset/value_operator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_operator.imageset/value_operator.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_operator.imageset/value_operator@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_operator.imageset/value_operator@2x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_operator.imageset/value_operator@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_operator.imageset/value_operator@3x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_operator_old.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "value_operator_old.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "value_operator_old@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "value_operator_old@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_operator_old.imageset/value_operator_old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_operator_old.imageset/value_operator_old.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_operator_old.imageset/value_operator_old@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_operator_old.imageset/value_operator_old@2x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_operator_old.imageset/value_operator_old@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_operator_old.imageset/value_operator_old@3x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_subject.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "value_subject.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "value_subject@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "value_subject@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_subject.imageset/value_subject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_subject.imageset/value_subject.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_subject.imageset/value_subject@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_subject.imageset/value_subject@2x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_subject.imageset/value_subject@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_subject.imageset/value_subject@3x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_subject_old.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "value_subject_old.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "value_subject_old@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "value_subject_old@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_subject_old.imageset/value_subject_old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_subject_old.imageset/value_subject_old.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_subject_old.imageset/value_subject_old@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_subject_old.imageset/value_subject_old@2x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_subject_old.imageset/value_subject_old@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cocoatoucher/CombineOperators/68b1f4d96a264472e0c3a3dfa40134e1dfb68d69/Sources/CombineOperatorsCore/Resources/Media.xcassets/Shared/value_subject_old.imageset/value_subject_old@3x.png -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Collecting/OperatorAllSatisfyViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorAllSatisfyViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorAllSatisfyViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "allSatisfy", description: "value % 2 = 0")] 33 | 34 | operatorInfo = "Publishes a single Boolean value that indicates whether all received elements pass a given predicate." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let allSatisfy = subject 40 | .allSatisfy { value -> Bool in 41 | return Int(value) % 2 == 0 42 | } 43 | 44 | allSatisfy 45 | .sink( 46 | receiveValue: { value in 47 | display("\\(value)")) 48 | } 49 | ) 50 | """ 51 | } 52 | 53 | override func setupBindings() { 54 | let subject1 = Subject( 55 | title: "Subject", 56 | inputValues: ["2", "3", nil] 57 | ) 58 | 59 | subjects = [subject1] 60 | 61 | let last = subject1.trackedPublisher! 62 | .allSatisfy { value -> Bool in 63 | guard 64 | let value = value, 65 | let valueInt = Int(value) 66 | else { 67 | return false 68 | } 69 | return valueInt % 2 == 0 70 | } 71 | 72 | subscription = last 73 | .handleEvents( 74 | receiveSubscription: { [weak self] _ in 75 | guard let self = self else { return } 76 | self.updateOperator(.subscription) 77 | }, receiveCancel: { [weak self] in 78 | guard let self = self else { return } 79 | self.updateOperator(.cancel) 80 | }, receiveRequest: { [weak self] _ in 81 | guard let self = self else { return } 82 | self.updateOperator(.request) 83 | } 84 | ) 85 | .sink( 86 | receiveCompletion: { [weak self] completion in 87 | guard let self = self else { return } 88 | self.setCompletionFromOperator(completion: completion) 89 | }, receiveValue: { [weak self] value in 90 | guard let self = self else { return } 91 | self.setValueFromOperator(value: "\(value)") 92 | } 93 | ) 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Collecting/OperatorContainsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorContainsViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorContainsViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "contains", description: "🌟")] 33 | 34 | operatorInfo = "Publishes a Boolean value upon receiving an element equal to the argument." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let contains = subject 40 | .contains("🌟") 41 | 42 | contains 43 | .sink( 44 | receiveValue: { value in 45 | display("\\(value)")) 46 | } 47 | ) 48 | """ 49 | } 50 | 51 | override func setupBindings() { 52 | let subject1 = Subject( 53 | title: "Subject", 54 | inputValues: ["🌟", "✅", nil] 55 | ) 56 | 57 | subjects = [subject1] 58 | 59 | let last = subject1.trackedPublisher!.contains("🌟") 60 | 61 | subscription = last 62 | .handleEvents( 63 | receiveSubscription: { [weak self] _ in 64 | guard let self = self else { return } 65 | self.updateOperator(.subscription) 66 | }, receiveCancel: { [weak self] in 67 | guard let self = self else { return } 68 | self.updateOperator(.cancel) 69 | }, receiveRequest: { [weak self] _ in 70 | guard let self = self else { return } 71 | self.updateOperator(.request) 72 | } 73 | ) 74 | .sink( 75 | receiveCompletion: { [weak self] completion in 76 | guard let self = self else { return } 77 | self.setCompletionFromOperator(completion: completion) 78 | }, receiveValue: { [weak self] value in 79 | guard let self = self else { return } 80 | self.setValueFromOperator(value: "\(value)") 81 | } 82 | ) 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Collecting/OperatorCountViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorCountViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorCountViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "count", description: nil)] 33 | 34 | operatorInfo = "Publishes the number of elements received from the upstream publisher." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let count = subject.count() 40 | 41 | count 42 | .sink( 43 | receiveValue: { value in 44 | display("\\(value)")) 45 | } 46 | ) 47 | """ 48 | } 49 | 50 | override func setupBindings() { 51 | let subject1 = Subject( 52 | title: "Subject", 53 | inputValues: ["🌟", "✅", nil] 54 | ) 55 | 56 | subjects = [subject1] 57 | 58 | let count = subject1.trackedPublisher!.count() 59 | 60 | subscription = count 61 | .handleEvents( 62 | receiveSubscription: { [weak self] _ in 63 | guard let self = self else { return } 64 | self.updateOperator(.subscription) 65 | }, receiveCancel: { [weak self] in 66 | guard let self = self else { return } 67 | self.updateOperator(.cancel) 68 | }, receiveRequest: { [weak self] _ in 69 | guard let self = self else { return } 70 | self.updateOperator(.request) 71 | } 72 | ) 73 | .sink( 74 | receiveCompletion: { [weak self] completion in 75 | guard let self = self else { return } 76 | self.setCompletionFromOperator(completion: completion) 77 | }, receiveValue: { [weak self] value in 78 | guard let self = self else { return } 79 | self.setValueFromOperator(value: "\(value)") 80 | } 81 | ) 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Collecting/OperatorMinViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorMinViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorMinViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "min", description: nil)] 33 | 34 | operatorInfo = "Publishes the minimum value received from the upstream publisher, after it finishes." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let min = subject 40 | .min { left, right -> Bool in 41 | return Int(left) < Int(right) 42 | } 43 | 44 | min 45 | .sink( 46 | receiveValue: { value in 47 | display(value) 48 | } 49 | ) 50 | """ 51 | } 52 | 53 | override func setupBindings() { 54 | let subject1 = Subject( 55 | title: "Subject", 56 | inputValues: ["3", "5", nil] 57 | ) 58 | 59 | subjects = [subject1] 60 | 61 | let last = subject1.trackedPublisher!.min { left, right -> Bool in 62 | guard let left = left, let leftInt = Int(left) else { 63 | return false 64 | } 65 | guard let right = right, let rightInt = Int(right) else { 66 | return false 67 | } 68 | return Int(leftInt) < Int(rightInt) 69 | } 70 | 71 | subscription = last 72 | .handleEvents( 73 | receiveSubscription: { [weak self] _ in 74 | guard let self = self else { return } 75 | self.updateOperator(.subscription) 76 | }, receiveCancel: { [weak self] in 77 | guard let self = self else { return } 78 | self.updateOperator(.cancel) 79 | }, receiveRequest: { [weak self] _ in 80 | guard let self = self else { return } 81 | self.updateOperator(.request) 82 | } 83 | ) 84 | .sink( 85 | receiveCompletion: { [weak self] completion in 86 | guard let self = self else { return } 87 | self.setCompletionFromOperator(completion: completion) 88 | }, receiveValue: { [weak self] value in 89 | guard let self = self else { return } 90 | self.setValueFromOperator(value: value) 91 | } 92 | ) 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Collecting/OperatorOutputAtViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorOutputAtViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorOutputAtViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "outputAt", description: "index 1")] 33 | 34 | operatorInfo = "Publishes a specific element, indicated by its index in the sequence of published elements." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let outputAt = subject 40 | .output(at: 1) 41 | 42 | outputAt 43 | .sink( 44 | receiveValue: { value in 45 | display(value ?? "none") 46 | } 47 | ) 48 | """ 49 | } 50 | 51 | override func setupBindings() { 52 | let subject1 = Subject( 53 | title: "Subject", 54 | inputValues: ["🌟", "✅", nil] 55 | ) 56 | 57 | subjects = [subject1] 58 | 59 | let outputAt = subject1.trackedPublisher!.output(at: 1) 60 | 61 | subscription = outputAt 62 | .handleEvents( 63 | receiveSubscription: { [weak self] _ in 64 | guard let self = self else { return } 65 | self.updateOperator(.subscription) 66 | }, receiveCancel: { [weak self] in 67 | guard let self = self else { return } 68 | self.updateOperator(.cancel) 69 | }, receiveRequest: { [weak self] _ in 70 | guard let self = self else { return } 71 | self.updateOperator(.request) 72 | } 73 | ) 74 | .sink( 75 | receiveCompletion: { [weak self] completion in 76 | guard let self = self else { return } 77 | self.setCompletionFromOperator(completion: completion) 78 | }, receiveValue: { [weak self] value in 79 | guard let self = self else { return } 80 | self.setValueFromOperator(value: value ?? "none") 81 | } 82 | ) 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Collecting/OperatorReduceViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorReduceViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorReduceViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "reduce", description: "➡️ + values")] 33 | 34 | operatorInfo = "Applies a closure that collects each element of a stream and publishes a final result upon completion." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let reduce = subject 40 | .reduce("➡️") { reduced, value -> String? in 41 | return (reduced ?? "") + (value ?? "nil") 42 | } 43 | 44 | reduce 45 | .sink( 46 | receiveValue: { value in 47 | display(value) 48 | } 49 | ) 50 | """ 51 | } 52 | 53 | override func setupBindings() { 54 | 55 | let subject1 = Subject( 56 | title: "Subject", 57 | inputValues: ["🌟", "✅", nil] 58 | ) 59 | 60 | subjects = [subject1] 61 | 62 | let reduce = subject1.trackedPublisher! 63 | .reduce("➡️") { reduced, value -> String? in 64 | return (reduced ?? "") + (value ?? "nil") 65 | } 66 | 67 | subscription = reduce 68 | .handleEvents( 69 | receiveSubscription: { [weak self] _ in 70 | guard let self = self else { return } 71 | self.updateOperator(.subscription) 72 | }, receiveCancel: { [weak self] in 73 | guard let self = self else { return } 74 | self.updateOperator(.cancel) 75 | }, receiveRequest: { [weak self] _ in 76 | guard let self = self else { return } 77 | self.updateOperator(.request) 78 | } 79 | ) 80 | .sink( 81 | receiveCompletion: { [weak self] completion in 82 | guard let self = self else { return } 83 | self.setCompletionFromOperator(completion: completion) 84 | }, receiveValue: { [weak self] value in 85 | guard let self = self else { return } 86 | self.setValueFromOperator(value: value) 87 | } 88 | ) 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Combining/OperatorAppendViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorAppendViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorAppendViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "append", description: "S2 -> S1")] 33 | 34 | operatorInfo = "Appends a publisher’s output with the elements emitted by the given publisher." 35 | 36 | operatorCode = """ 37 | let subject1 = PassthroughSubject() 38 | let subject2 = PassthroughSubject() 39 | 40 | let append = subject1 41 | .append(subject2) 42 | 43 | append 44 | .sink( 45 | receiveValue: { value in 46 | display(value) 47 | } 48 | ) 49 | """ 50 | } 51 | 52 | override func setupBindings() { 53 | let subject1 = Subject( 54 | title: "Subject 1", 55 | inputValues: ["🌟", "✅", nil] 56 | ) 57 | 58 | let subject2 = Subject( 59 | title: "Subject 2", 60 | inputValues: ["😎", "😱", nil] 61 | ) 62 | 63 | subjects = [subject1, subject2] 64 | 65 | let append = subject1.trackedPublisher! 66 | .append(subject2.trackedPublisher!) 67 | 68 | subscription = append 69 | .handleEvents( 70 | receiveSubscription: { [weak self] _ in 71 | guard let self = self else { return } 72 | self.updateOperator(.subscription) 73 | }, receiveCancel: { [weak self] in 74 | guard let self = self else { return } 75 | self.updateOperator(.cancel) 76 | }, receiveRequest: { [weak self] _ in 77 | guard let self = self else { return } 78 | self.updateOperator(.request) 79 | } 80 | ) 81 | .sink( 82 | receiveCompletion: { [weak self] completion in 83 | guard let self = self else { return } 84 | self.setCompletionFromOperator(completion: completion) 85 | }, receiveValue: { [weak self] value in 86 | guard let self = self else { return } 87 | self.setValueFromOperator(value: value) 88 | } 89 | ) 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Combining/OperatorCombineLatestViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorCombineLatestViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorCombineLatestViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "combineLatest", description: nil)] 33 | 34 | operatorInfo = "Subscribes to an additional publisher and publishes a tuple upon receiving output from either publisher." 35 | 36 | operatorCode = """ 37 | let subject1 = PassthroughSubject() 38 | let subject2 = PassthroughSubject() 39 | 40 | let combineLatest = subject1 41 | .combineLatest(subject2) 42 | 43 | combineLatest 44 | .sink( 45 | receiveValue: { first, second in 46 | display("\\(first ?? "nil") \\(second ?? "nil")") 47 | } 48 | ) 49 | """ 50 | } 51 | 52 | override func setupBindings() { 53 | let subject1 = Subject( 54 | title: "Subject 1", 55 | inputValues: ["🌟", "✅", nil] 56 | ) 57 | let subject2 = Subject( 58 | title: "Subject 2", 59 | inputValues: ["😎", "😱", nil] 60 | ) 61 | 62 | subjects = [subject1, subject2] 63 | 64 | let combined = subject1.trackedPublisher! 65 | .combineLatest(subject2.trackedPublisher!) 66 | 67 | subscription = combined 68 | .handleEvents( 69 | receiveSubscription: { [weak self] _ in 70 | guard let self = self else { return } 71 | self.updateOperator(.subscription) 72 | }, receiveCancel: { [weak self] in 73 | guard let self = self else { return } 74 | self.updateOperator(.cancel) 75 | }, receiveRequest: { [weak self] _ in 76 | guard let self = self else { return } 77 | self.updateOperator(.request) 78 | } 79 | ) 80 | .sink( 81 | receiveCompletion: { [weak self] completion in 82 | guard let self = self else { return } 83 | self.setCompletionFromOperator(completion: completion) 84 | }, 85 | receiveValue: { [weak self] firstValue, secondValue in 86 | guard let self = self else { return } 87 | self.setValueFromOperator(value: "\(firstValue ?? "nil") \(secondValue ?? "nil")") 88 | } 89 | ) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Combining/OperatorDropUntilOutputFromViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorDropUntilOutputFromViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorDropUntilOutputFromViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "dropUntilOutput", description: "Upstream: S1")] 33 | 34 | operatorInfo = "Ignores elements from the upstream publisher until it receives an element from a second publisher." 35 | 36 | operatorCode = """ 37 | let subject1 = PassthroughSubject() 38 | let subject2 = PassthroughSubject() 39 | 40 | let dropUntilOutputFrom = subject1 41 | .drop(untilOutputFrom: subject2) 42 | 43 | dropUntilOutputFrom 44 | .sink( 45 | receiveValue: { value in 46 | display(value) 47 | } 48 | ) 49 | """ 50 | } 51 | 52 | override func setupBindings() { 53 | let subject1 = Subject( 54 | title: "Subject 1", 55 | inputValues: ["🌟", "✅", nil] 56 | ) 57 | 58 | let subject2 = Subject( 59 | title: "Subject 2", 60 | inputValues: ["😎", "😱", nil] 61 | ) 62 | 63 | subjects = [subject1, subject2] 64 | 65 | let dropUntil = subject1.trackedPublisher! 66 | .drop(untilOutputFrom: subject2.trackedPublisher!) 67 | 68 | subscription = dropUntil 69 | .handleEvents( 70 | receiveSubscription: { [weak self] _ in 71 | guard let self = self else { return } 72 | self.updateOperator(.subscription) 73 | }, receiveCancel: { [weak self] in 74 | guard let self = self else { return } 75 | self.updateOperator(.cancel) 76 | }, receiveRequest: { [weak self] _ in 77 | guard let self = self else { return } 78 | self.updateOperator(.request) 79 | } 80 | ) 81 | .sink( 82 | receiveCompletion: { [weak self] completion in 83 | guard let self = self else { return } 84 | self.setCompletionFromOperator(completion: completion) 85 | }, receiveValue: { [weak self] value in 86 | guard let self = self else { return } 87 | self.setValueFromOperator(value: value) 88 | } 89 | ) 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Combining/OperatorFlatMapViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorFlatMapViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorFlatMapViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "flatMap", description: "S1 -> S2")] 33 | 34 | operatorInfo = "Transforms all elements from an upstream publisher into a new or existing publisher." 35 | 36 | operatorCode = """ 37 | let subject1 = PassthroughSubject() 38 | let subject2 = PassthroughSubject() 39 | 40 | let flatMap = subject1 41 | .flatMap { _ in 42 | return subject2 43 | } 44 | 45 | flatMap 46 | .sink( 47 | receiveValue: { value in 48 | display(value) 49 | } 50 | ) 51 | """ 52 | } 53 | 54 | override func setupBindings() { 55 | let subject1 = Subject( 56 | title: "Subject 1", 57 | inputValues: ["🌟", "✅", nil] 58 | ) 59 | 60 | let subject2 = Subject( 61 | title: "Subject 2", 62 | inputValues: ["😎", "😱", nil] 63 | ) 64 | 65 | subjects = [subject1, subject2] 66 | 67 | let flatMap = subject1.trackedPublisher! 68 | .flatMap { _ -> AnyPublisher in 69 | return subject2.trackedPublisher! 70 | } 71 | 72 | subscription = flatMap 73 | .handleEvents( 74 | receiveSubscription: { [weak self] _ in 75 | guard let self = self else { return } 76 | self.updateOperator(.subscription) 77 | }, receiveCancel: { [weak self] in 78 | guard let self = self else { return } 79 | self.updateOperator(.cancel) 80 | }, receiveRequest: { [weak self] _ in 81 | guard let self = self else { return } 82 | self.updateOperator(.request) 83 | } 84 | ) 85 | .sink( 86 | receiveCompletion: { [weak self] completion in 87 | guard let self = self else { return } 88 | self.setCompletionFromOperator(completion: completion) 89 | }, receiveValue: { [weak self] value in 90 | guard let self = self else { return } 91 | self.setValueFromOperator(value: value) 92 | } 93 | ) 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Combining/OperatorMergeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorMergeViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorMergeViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "merge", description: nil)] 33 | 34 | operatorInfo = "Combines elements from a publisher with those from another publisher of the same type, delivering an interleaved sequence of elements." 35 | 36 | operatorCode = """ 37 | let subject1 = PassthroughSubject() 38 | let subject2 = PassthroughSubject() 39 | 40 | let merge = subject1 41 | .merge(with: subject2) 42 | 43 | merge 44 | .sink( 45 | receiveValue: { value in 46 | display(value) 47 | } 48 | ) 49 | """ 50 | } 51 | 52 | override func setupBindings() { 53 | let subject1 = Subject( 54 | title: "Subject 1", 55 | inputValues: ["🌟", "✅", nil] 56 | ) 57 | 58 | let subject2 = Subject( 59 | title: "Subject 2", 60 | inputValues: ["😎", "😱", nil] 61 | ) 62 | 63 | subjects = [subject1, subject2] 64 | 65 | let merge = subject1.trackedPublisher! 66 | .merge(with: subject2.trackedPublisher!) 67 | 68 | subscription = merge 69 | .handleEvents( 70 | receiveSubscription: { [weak self] _ in 71 | guard let self = self else { return } 72 | self.updateOperator(.subscription) 73 | }, receiveCancel: { [weak self] in 74 | guard let self = self else { return } 75 | self.updateOperator(.cancel) 76 | }, receiveRequest: { [weak self] _ in 77 | guard let self = self else { return } 78 | self.updateOperator(.request) 79 | } 80 | ) 81 | .sink( 82 | receiveCompletion: { [weak self] completion in 83 | guard let self = self else { return } 84 | self.setCompletionFromOperator(completion: completion) 85 | }, receiveValue: { [weak self] value in 86 | guard let self = self else { return } 87 | self.setValueFromOperator(value: value) 88 | } 89 | ) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Combining/OperatorPrefixUntilOutputFromViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorPrefixUntilOutputFromViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorPrefixUntilOutputFromViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "prefixUntilOutput", description: "from S2")] 33 | 34 | operatorInfo = "Republishes elements until another publisher emits an element." 35 | 36 | operatorCode = """ 37 | let subject1 = PassthroughSubject() 38 | let subject2 = PassthroughSubject() 39 | 40 | let prefixUntilOutputFrom = subject1 41 | .prefix(untilOutputFrom: subject2) 42 | 43 | prefixUntilOutputFrom 44 | .sink( 45 | receiveValue: { value in 46 | display(value) 47 | } 48 | ) 49 | """ 50 | } 51 | 52 | override func setupBindings() { 53 | let subject1 = Subject( 54 | title: "Subject 1", 55 | inputValues: ["🌟", "✅", nil] 56 | ) 57 | 58 | let subject2 = Subject( 59 | title: "Subject 2", 60 | inputValues: ["😎", "😱", nil] 61 | ) 62 | 63 | subjects = [subject1, subject2] 64 | 65 | let prefixUntil = subject1.trackedPublisher! 66 | .prefix(untilOutputFrom: subject2.trackedPublisher!) 67 | 68 | subscription = prefixUntil 69 | .handleEvents( 70 | receiveSubscription: { [weak self] _ in 71 | guard let self = self else { return } 72 | self.updateOperator(.subscription) 73 | }, receiveCancel: { [weak self] in 74 | guard let self = self else { return } 75 | self.updateOperator(.cancel) 76 | }, receiveRequest: { [weak self] _ in 77 | guard let self = self else { return } 78 | self.updateOperator(.request) 79 | } 80 | ) 81 | .sink( 82 | receiveCompletion: { [weak self] completion in 83 | guard let self = self else { return } 84 | self.setCompletionFromOperator(completion: completion) 85 | }, receiveValue: { [weak self] value in 86 | guard let self = self else { return } 87 | self.setValueFromOperator(value: value) 88 | } 89 | ) 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Combining/OperatorPrependViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorPrependViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorPrependViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "prepend", description: "S2 -> S1")] 33 | 34 | operatorInfo = "Prefixes a publisher’s output with the elements emitted by the given publisher." 35 | 36 | operatorCode = """ 37 | let subject1 = PassthroughSubject() 38 | let subject2 = PassthroughSubject() 39 | 40 | let prepend = subject1 41 | .prepend(subject2) 42 | 43 | prepend 44 | .sink( 45 | receiveValue: { value in 46 | display(value) 47 | } 48 | ) 49 | """ 50 | } 51 | 52 | override func setupBindings() { 53 | let subject1 = Subject( 54 | title: "Subject 1", 55 | inputValues: ["🌟", "✅", nil] 56 | ) 57 | 58 | let subject2 = Subject( 59 | title: "Subject 2", 60 | inputValues: ["😎", "😱", nil] 61 | ) 62 | 63 | subjects = [subject1, subject2] 64 | 65 | let prepend = subject1.trackedPublisher! 66 | .prepend(subject2.trackedPublisher!) 67 | 68 | subscription = prepend 69 | .handleEvents( 70 | receiveSubscription: { [weak self] _ in 71 | guard let self = self else { return } 72 | self.updateOperator(.subscription) 73 | }, receiveCancel: { [weak self] in 74 | guard let self = self else { return } 75 | self.updateOperator(.cancel) 76 | }, receiveRequest: { [weak self] _ in 77 | guard let self = self else { return } 78 | self.updateOperator(.request) 79 | } 80 | ) 81 | .sink( 82 | receiveCompletion: { [weak self] completion in 83 | guard let self = self else { return } 84 | self.setCompletionFromOperator(completion: completion) 85 | }, receiveValue: { [weak self] value in 86 | guard let self = self else { return } 87 | self.setValueFromOperator(value: value) 88 | } 89 | ) 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Combining/OperatorZipViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorZipViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorZipViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "zip", description: nil)] 33 | 34 | operatorInfo = "Combines elements from another publisher and deliver pairs of elements as tuples." 35 | 36 | operatorCode = """ 37 | let subject1 = PassthroughSubject() 38 | let subject2 = PassthroughSubject() 39 | 40 | let zip = subject1 41 | .zip(subject2) 42 | 43 | zip 44 | .sink( 45 | receiveValue: { values in 46 | let result = "\\(values.0 ?? "nil") \\(values.1 ?? "nil")" 47 | display(result) 48 | } 49 | ) 50 | """ 51 | } 52 | 53 | override func setupBindings() { 54 | 55 | let subject1 = Subject( 56 | title: "Subject 1", 57 | inputValues: ["🌟", "✅", nil] 58 | ) 59 | 60 | let subject2 = Subject( 61 | title: "Subject 2", 62 | inputValues: ["😎", "😱", nil] 63 | ) 64 | 65 | subjects = [subject1, subject2] 66 | 67 | let zip = subject1.trackedPublisher! 68 | .zip(subject2.trackedPublisher!) 69 | 70 | subscription = zip 71 | .handleEvents( 72 | receiveSubscription: { [weak self] _ in 73 | guard let self = self else { return } 74 | self.updateOperator(.subscription) 75 | }, receiveCancel: { [weak self] in 76 | guard let self = self else { return } 77 | self.updateOperator(.cancel) 78 | }, receiveRequest: { [weak self] _ in 79 | guard let self = self else { return } 80 | self.updateOperator(.request) 81 | } 82 | ) 83 | .sink( 84 | receiveCompletion: { [weak self] completion in 85 | guard let self = self else { return } 86 | self.setCompletionFromOperator(completion: completion) 87 | }, receiveValue: { [weak self] values in 88 | guard let self = self else { return } 89 | let result = "\(values.0 ?? "nil") \(values.1 ?? "nil")" 90 | self.setValueFromOperator(value: "\(result)") 91 | } 92 | ) 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Error handling/OperatorCatchErrorViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorCatchErrorViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorCatchErrorViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "catch", description: "to 👀")] 33 | 34 | operatorInfo = "Handles errors from an upstream publisher by replacing it with another publisher." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let catchError = subject 40 | .catch { error in 41 | Just("👀") 42 | } 43 | 44 | catchError 45 | .sink( 46 | receiveValue: { value in 47 | display(value) 48 | } 49 | ) 50 | """ 51 | } 52 | 53 | override func setupBindings() { 54 | let subject = Subject( 55 | title: "Subject", 56 | inputValues: ["🌟", "✅", nil] 57 | ) 58 | 59 | subjects = [subject] 60 | 61 | let catchError = subject.trackedPublisher! 62 | .catch { error in 63 | Just("👀") 64 | } 65 | 66 | subscription = catchError 67 | .handleEvents( 68 | receiveSubscription: { [weak self] _ in 69 | guard let self = self else { return } 70 | self.updateOperator(.subscription) 71 | }, receiveCancel: { [weak self] in 72 | guard let self = self else { return } 73 | self.updateOperator(.cancel) 74 | }, receiveRequest: { [weak self] _ in 75 | guard let self = self else { return } 76 | self.updateOperator(.request) 77 | } 78 | ) 79 | .sink( 80 | receiveCompletion: { [weak self] completion in 81 | guard let self = self else { return } 82 | self.setCompletionFromOperator(completion: completion) 83 | }, receiveValue: { [weak self] value in 84 | guard let self = self else { return } 85 | self.setValueFromOperator(value: value) 86 | } 87 | ) 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Error handling/OperatorMapErrorViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorMapErrorViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorMapErrorViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "mapError", description: "to ☠️ error")] 33 | 34 | operatorInfo = "Converts any failure from the upstream publisher into a new error." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let mapError = subject 40 | .mapError { error -> Error in 41 | return TestError.mapped 42 | } 43 | 44 | mapError 45 | .sink( 46 | receiveValue: { value in 47 | display(value) 48 | } 49 | ) 50 | """ 51 | } 52 | 53 | override func setupBindings() { 54 | let subject = Subject( 55 | title: "Subject", 56 | inputValues: ["🌟", "✅", nil] 57 | ) 58 | 59 | subjects = [subject] 60 | 61 | let mapError = subject.trackedPublisher! 62 | .mapError { error -> Error in 63 | return TestError.mapped 64 | } 65 | 66 | subscription = mapError 67 | .handleEvents( 68 | receiveSubscription: { [weak self] _ in 69 | guard let self = self else { return } 70 | self.updateOperator(.subscription) 71 | }, receiveCancel: { [weak self] in 72 | guard let self = self else { return } 73 | self.updateOperator(.cancel) 74 | }, receiveRequest: { [weak self] _ in 75 | guard let self = self else { return } 76 | self.updateOperator(.request) 77 | } 78 | ) 79 | .sink( 80 | receiveCompletion: { [weak self] completion in 81 | guard let self = self else { return } 82 | self.setCompletionFromOperator(completion: completion) 83 | }, receiveValue: { [weak self] value in 84 | guard let self = self else { return } 85 | self.setValueFromOperator(value: value) 86 | } 87 | ) 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Error handling/OperatorReplaceErrorViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorReplaceErrorViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorReplaceErrorViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "replaceError", description: "with 👻")] 33 | 34 | operatorInfo = "Replaces any errors in the stream with the provided element." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let replaceError = subject 40 | .replaceError(with: "👻") 41 | 42 | replaceError 43 | .sink( 44 | receiveValue: { value in 45 | display(value) 46 | } 47 | ) 48 | """ 49 | } 50 | 51 | override func setupBindings() { 52 | let subject = Subject( 53 | title: "Subject", 54 | inputValues: ["🌟", "✅", nil] 55 | ) 56 | 57 | subjects = [subject] 58 | 59 | let replaceError = subject.trackedPublisher! 60 | .replaceError(with: "👻") 61 | 62 | subscription = replaceError 63 | .handleEvents( 64 | receiveSubscription: { [weak self] _ in 65 | guard let self = self else { return } 66 | self.updateOperator(.subscription) 67 | }, receiveCancel: { [weak self] in 68 | guard let self = self else { return } 69 | self.updateOperator(.cancel) 70 | }, receiveRequest: { [weak self] _ in 71 | guard let self = self else { return } 72 | self.updateOperator(.request) 73 | } 74 | ) 75 | .sink( 76 | receiveCompletion: { [weak self] completion in 77 | guard let self = self else { return } 78 | self.setCompletionFromOperator(completion: completion) 79 | }, receiveValue: { [weak self] value in 80 | guard let self = self else { return } 81 | self.setValueFromOperator(value: value) 82 | } 83 | ) 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Filtering/OperatorCompactMapViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorCompactMapViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorCompactMapViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "compactMap", description: "value + 💃")] 33 | 34 | operatorInfo = "Calls a closure with each received element and publishes any returned optional that has a value." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let compactMap = subject 40 | .compactMap { value -> String? in 41 | if let value = value { 42 | return "\\(value) 💃" 43 | } 44 | return nil 45 | } 46 | 47 | compactMap 48 | .sink( 49 | receiveValue: { value in 50 | display(value) 51 | } 52 | ) 53 | """ 54 | } 55 | 56 | override func setupBindings() { 57 | let subject1 = Subject( 58 | title: "Subject", 59 | inputValues: ["🌟", "✅", nil] 60 | ) 61 | 62 | subjects = [subject1] 63 | 64 | let compactMap = subject1.trackedPublisher! 65 | .compactMap { value -> String? in 66 | if let value = value { 67 | return "\(value) 💃" 68 | } 69 | return nil 70 | } 71 | 72 | subscription = compactMap 73 | .handleEvents( 74 | receiveSubscription: { [weak self] _ in 75 | guard let self = self else { return } 76 | self.updateOperator(.subscription) 77 | }, receiveCancel: { [weak self] in 78 | guard let self = self else { return } 79 | self.updateOperator(.cancel) 80 | }, receiveRequest: { [weak self] _ in 81 | guard let self = self else { return } 82 | self.updateOperator(.request) 83 | } 84 | ) 85 | .sink( 86 | receiveCompletion: { [weak self] completion in 87 | guard let self = self else { return } 88 | self.setCompletionFromOperator(completion: completion) 89 | }, receiveValue: { [weak self] value in 90 | guard let self = self else { return } 91 | self.setValueFromOperator(value: value) 92 | } 93 | ) 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Filtering/OperatorDropFirstViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorDropFirstViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorDropFirstViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "dropFirst", description: "first 2")] 33 | 34 | operatorInfo = "Omits the specified number of elements before republishing subsequent elements." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let dropFirst = subject 40 | .dropFirst(2) 41 | 42 | dropFirst 43 | .sink( 44 | receiveValue: { value in 45 | display(value) 46 | } 47 | ) 48 | """ 49 | } 50 | 51 | override func setupBindings() { 52 | let subject1 = Subject( 53 | title: "Subject", 54 | inputValues: ["🌟", "✅", nil] 55 | ) 56 | 57 | subjects = [subject1] 58 | 59 | let dropFirst = subject1.trackedPublisher! 60 | .dropFirst(2) 61 | 62 | subscription = dropFirst 63 | .handleEvents( 64 | receiveSubscription: { [weak self] _ in 65 | guard let self = self else { return } 66 | self.updateOperator(.subscription) 67 | }, receiveCancel: { [weak self] in 68 | guard let self = self else { return } 69 | self.updateOperator(.cancel) 70 | }, receiveRequest: { [weak self] _ in 71 | guard let self = self else { return } 72 | self.updateOperator(.request) 73 | } 74 | ) 75 | .sink( 76 | receiveCompletion: { [weak self] completion in 77 | guard let self = self else { return } 78 | self.setCompletionFromOperator(completion: completion) 79 | }, receiveValue: { [weak self] value in 80 | guard let self = self else { return } 81 | self.setValueFromOperator(value: value) 82 | } 83 | ) 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Filtering/OperatorDropWhileViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorDropWhileViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorDropWhileViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "dropWhile", description: "value == 🌟")] 33 | 34 | operatorInfo = "Omits elements from the upstream publisher until a given closure returns false, before republishing all remaining elements." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let dropWhile = subject 40 | .drop(while: { 41 | $0 == "🌟" 42 | }) 43 | 44 | dropWhile 45 | .sink( 46 | receiveValue: { value in 47 | display(value) 48 | } 49 | ) 50 | """ 51 | } 52 | 53 | override func setupBindings() { 54 | let subject1 = Subject( 55 | title: "Subject", 56 | inputValues: ["🌟", "✅", nil] 57 | ) 58 | 59 | subjects = [subject1] 60 | 61 | let dropWhile = subject1.trackedPublisher! 62 | .drop(while: { 63 | $0 == "🌟" 64 | }) 65 | 66 | subscription = dropWhile 67 | .handleEvents( 68 | receiveSubscription: { [weak self] _ in 69 | guard let self = self else { return } 70 | self.updateOperator(.subscription) 71 | }, receiveCancel: { [weak self] in 72 | guard let self = self else { return } 73 | self.updateOperator(.cancel) 74 | }, receiveRequest: { [weak self] _ in 75 | guard let self = self else { return } 76 | self.updateOperator(.request) 77 | } 78 | ) 79 | .sink( 80 | receiveCompletion: { [weak self] completion in 81 | guard let self = self else { return } 82 | self.setCompletionFromOperator(completion: completion) 83 | }, receiveValue: { [weak self] value in 84 | guard let self = self else { return } 85 | self.setValueFromOperator(value: value) 86 | } 87 | ) 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Filtering/OperatorFilterViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorFilterViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorFilterViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "filter", description: "value == ✅")] 33 | 34 | operatorInfo = "Republishes all elements that match a provided closure." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let filter = subject 40 | .filter { value -> Bool in 41 | return value == "✅" 42 | } 43 | 44 | filter 45 | .sink( 46 | receiveValue: { value in 47 | display(value) 48 | } 49 | ) 50 | """ 51 | } 52 | 53 | override func setupBindings() { 54 | let subject1 = Subject( 55 | title: "Subject", 56 | inputValues: ["🌟", "✅", nil] 57 | ) 58 | 59 | subjects = [subject1] 60 | 61 | let filter = subject1.trackedPublisher! 62 | .filter { value -> Bool in 63 | return value == "✅" 64 | } 65 | 66 | subscription = filter 67 | .handleEvents( 68 | receiveSubscription: { [weak self] _ in 69 | guard let self = self else { return } 70 | self.updateOperator(.subscription) 71 | }, receiveCancel: { [weak self] in 72 | guard let self = self else { return } 73 | self.updateOperator(.cancel) 74 | }, receiveRequest: { [weak self] _ in 75 | guard let self = self else { return } 76 | self.updateOperator(.request) 77 | } 78 | ) 79 | .sink( 80 | receiveCompletion: { [weak self] completion in 81 | guard let self = self else { return } 82 | self.setCompletionFromOperator(completion: completion) 83 | }, receiveValue: { [weak self] value in 84 | guard let self = self else { return } 85 | self.setValueFromOperator(value: value) 86 | } 87 | ) 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Filtering/OperatorFirstViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorFirstViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorFirstViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "first", description: nil)] 33 | 34 | operatorInfo = "Publishes the first element of a stream, then finishes." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let first = subject.first() 40 | 41 | first 42 | .sink( 43 | receiveValue: { value in 44 | display(value) 45 | } 46 | ) 47 | """ 48 | } 49 | 50 | override func setupBindings() { 51 | let subject1 = Subject( 52 | title: "Subject", 53 | inputValues: ["🌟", "✅", nil] 54 | ) 55 | 56 | subjects = [subject1] 57 | 58 | let first = subject1.trackedPublisher! 59 | .first() 60 | 61 | subscription = first 62 | .handleEvents( 63 | receiveSubscription: { [weak self] _ in 64 | guard let self = self else { return } 65 | self.updateOperator(.subscription) 66 | }, receiveCancel: { [weak self] in 67 | guard let self = self else { return } 68 | self.updateOperator(.cancel) 69 | }, receiveRequest: { [weak self] _ in 70 | guard let self = self else { return } 71 | self.updateOperator(.request) 72 | } 73 | ) 74 | .sink( 75 | receiveCompletion: { [weak self] completion in 76 | guard let self = self else { return } 77 | self.setCompletionFromOperator(completion: completion) 78 | }, receiveValue: { [weak self] value in 79 | guard let self = self else { return } 80 | self.setValueFromOperator(value: value) 81 | } 82 | ) 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Filtering/OperatorIgnoreOutputViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorIgnoreOutputViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorIgnoreOutputViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "ignoreOutput", description: nil)] 33 | 34 | operatorInfo = "Ignores all upstream elements, but passes along a completion state (finished or failed). Try sending some values and then tapping `Finish`" 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let ignoreOutput = subject.ignoreOutput() 40 | 41 | ignoreOutput 42 | .sink( 43 | receiveValue: { value in 44 | display(value) 45 | } 46 | ) 47 | """ 48 | } 49 | 50 | override func setupBindings() { 51 | let subject1 = Subject( 52 | title: "Subject", 53 | inputValues: ["🌟", "✅", nil] 54 | ) 55 | 56 | subjects = [subject1] 57 | 58 | let ignored = subject1.trackedPublisher!.ignoreOutput() 59 | 60 | subscription = ignored 61 | .handleEvents( 62 | receiveSubscription: { [weak self] _ in 63 | guard let self = self else { return } 64 | self.updateOperator(.subscription) 65 | }, receiveCancel: { [weak self] in 66 | guard let self = self else { return } 67 | self.updateOperator(.cancel) 68 | }, receiveRequest: { [weak self] _ in 69 | guard let self = self else { return } 70 | self.updateOperator(.request) 71 | } 72 | ) 73 | .sink( 74 | receiveCompletion: { [weak self] completion in 75 | guard let self = self else { return } 76 | self.setCompletionFromOperator(completion: completion) 77 | }, 78 | receiveValue: { _ in } 79 | ) 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Filtering/OperatorLastViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorLastViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorLastViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "last", description: nil)] 33 | 34 | operatorInfo = "Only publishes the last element of a stream, after the stream finishes." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let last = subject.last() 40 | 41 | last 42 | .sink( 43 | receiveValue: { value in 44 | display(value) 45 | } 46 | ) 47 | """ 48 | } 49 | 50 | override func setupBindings() { 51 | let subject1 = Subject( 52 | title: "Subject", 53 | inputValues: ["🌟", "✅", nil] 54 | ) 55 | 56 | subjects = [subject1] 57 | 58 | let last = subject1.trackedPublisher!.last() 59 | 60 | subscription = last 61 | .handleEvents( 62 | receiveSubscription: { [weak self] _ in 63 | guard let self = self else { return } 64 | self.updateOperator(.subscription) 65 | }, receiveCancel: { [weak self] in 66 | guard let self = self else { return } 67 | self.updateOperator(.cancel) 68 | }, receiveRequest: { [weak self] _ in 69 | guard let self = self else { return } 70 | self.updateOperator(.request) 71 | } 72 | ) 73 | .sink( 74 | receiveCompletion: { [weak self] completion in 75 | guard let self = self else { return } 76 | self.setCompletionFromOperator(completion: completion) 77 | }, receiveValue: { [weak self] value in 78 | guard let self = self else { return } 79 | self.setValueFromOperator(value: value) 80 | } 81 | ) 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Filtering/OperatorPrefixViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorPrefixViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorPrefixViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "prefix", description: "length: 2")] 33 | 34 | operatorInfo = "Republishes elements up to the specified maximum count." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let prefix = subject.prefix(2) 40 | 41 | prefix 42 | .sink( 43 | receiveValue: { value in 44 | display(value) 45 | } 46 | ) 47 | """ 48 | } 49 | 50 | override func setupBindings() { 51 | let subject1 = Subject( 52 | title: "Subject", 53 | inputValues: ["🌟", "✅", nil] 54 | ) 55 | 56 | subjects = [subject1] 57 | 58 | let last = subject1.trackedPublisher!.prefix(2) 59 | 60 | subscription = last 61 | .handleEvents( 62 | receiveSubscription: { [weak self] _ in 63 | guard let self = self else { return } 64 | self.updateOperator(.subscription) 65 | }, receiveCancel: { [weak self] in 66 | guard let self = self else { return } 67 | self.updateOperator(.cancel) 68 | }, receiveRequest: { [weak self] _ in 69 | guard let self = self else { return } 70 | self.updateOperator(.request) 71 | } 72 | ) 73 | .sink( 74 | receiveCompletion: { [weak self] completion in 75 | guard let self = self else { return } 76 | self.setCompletionFromOperator(completion: completion) 77 | }, receiveValue: { [weak self] value in 78 | guard let self = self else { return } 79 | self.setValueFromOperator(value: value) 80 | } 81 | ) 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Filtering/OperatorRemoveDuplicatesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorRemoveDuplicatesViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorRemoveDuplicatesViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "remove\nDuplicates", description: nil)] 33 | 34 | operatorInfo = "Publishes only elements that don’t match the previous element." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let removeDuplicates = subject.removeDuplicates() 40 | 41 | removeDuplicates 42 | .sink( 43 | receiveValue: { value in 44 | display(value) 45 | } 46 | ) 47 | """ 48 | } 49 | 50 | override func setupBindings() { 51 | let subject1 = Subject( 52 | title: "Subject", 53 | inputValues: ["🌟", "✅", nil] 54 | ) 55 | 56 | subjects = [subject1] 57 | 58 | let removeDuplicates = subject1.trackedPublisher!.removeDuplicates() 59 | 60 | subscription = removeDuplicates 61 | .handleEvents( 62 | receiveSubscription: { [weak self] _ in 63 | guard let self = self else { return } 64 | self.updateOperator(.subscription) 65 | }, receiveCancel: { [weak self] in 66 | guard let self = self else { return } 67 | self.updateOperator(.cancel) 68 | }, receiveRequest: { [weak self] _ in 69 | guard let self = self else { return } 70 | self.updateOperator(.request) 71 | } 72 | ) 73 | .sink( 74 | receiveCompletion: { [weak self] completion in 75 | guard let self = self else { return } 76 | self.setCompletionFromOperator(completion: completion) 77 | }, receiveValue: { [weak self] value in 78 | guard let self = self else { return } 79 | self.setValueFromOperator(value: value) 80 | } 81 | ) 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Timing/OperatorDebounceViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorDebounceViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorDebounceViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | subjectOutputCountdown = 3 33 | 34 | operators = [Operator(name: "debounce", description: "3 seconds")] 35 | 36 | operatorInfo = "Publishes elements only after a specified time interval elapses between events." 37 | 38 | operatorCode = """ 39 | let subject = PassthroughSubject() 40 | 41 | let debounce = subject 42 | .debounce(for: 3, scheduler: DispatchQueue.main) 43 | 44 | debounce 45 | .sink( 46 | receiveValue: { value in 47 | display(value) 48 | } 49 | ) 50 | """ 51 | } 52 | 53 | override func setupBindings() { 54 | let subject1 = Subject( 55 | title: "Subject", 56 | inputValues: ["🌟", "✅", nil] 57 | ) 58 | 59 | subjects = [subject1] 60 | 61 | let debounce = subject1.trackedPublisher! 62 | .debounce(for: 3, scheduler: DispatchQueue.main) 63 | 64 | subscription = debounce 65 | .handleEvents( 66 | receiveSubscription: { [weak self] _ in 67 | guard let self = self else { return } 68 | self.updateOperator(.subscription) 69 | }, receiveCancel: { [weak self] in 70 | guard let self = self else { return } 71 | self.updateOperator(.cancel) 72 | }, receiveRequest: { [weak self] _ in 73 | guard let self = self else { return } 74 | self.updateOperator(.request) 75 | } 76 | ) 77 | .sink( 78 | receiveCompletion: { [weak self] completion in 79 | guard let self = self else { return } 80 | self.setCompletionFromOperator(completion: completion) 81 | }, receiveValue: { [weak self] value in 82 | guard let self = self else { return } 83 | self.setValueFromOperator(value: value) 84 | } 85 | ) 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Timing/OperatorDelayViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorDelayViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorDelayViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | subjectOutputCountdown = 2 33 | subjectValueRestartsCountdown = true 34 | countdownFinishRestartsCountdown = false 35 | 36 | operators = [Operator(name: "delay", description: "2 seconds")] 37 | 38 | operatorInfo = "Delays delivery of all output to the downstream receiver by a specified amount of time on a particular scheduler." 39 | 40 | operatorCode = """ 41 | let subject = PassthroughSubject() 42 | 43 | let delay = subject 44 | .delay(for: 2, scheduler: DispatchQueue.main) 45 | 46 | delay 47 | .sink( 48 | receiveValue: { value in 49 | display(value) 50 | } 51 | ) 52 | """ 53 | } 54 | 55 | override func setupBindings() { 56 | let subject1 = Subject( 57 | title: "Subject", 58 | inputValues: ["🌟", "✅", nil] 59 | ) 60 | 61 | subjects = [subject1] 62 | 63 | let delay = subject1.trackedPublisher! 64 | .delay(for: 2, scheduler: DispatchQueue.main) 65 | 66 | subscription = delay 67 | .handleEvents( 68 | receiveSubscription: { [weak self] _ in 69 | guard let self = self else { return } 70 | self.updateOperator(.subscription) 71 | }, receiveCancel: { [weak self] in 72 | guard let self = self else { return } 73 | self.updateOperator(.cancel) 74 | }, receiveRequest: { [weak self] _ in 75 | guard let self = self else { return } 76 | self.updateOperator(.request) 77 | } 78 | ) 79 | .sink( 80 | receiveCompletion: { [weak self] completion in 81 | guard let self = self else { return } 82 | self.setCompletionFromOperator(completion: completion) 83 | }, receiveValue: { [weak self] value in 84 | guard let self = self else { return } 85 | self.setValueFromOperator(value: value) 86 | } 87 | ) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Timing/OperatorMeasureIntervalViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorMeasureIntervalViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorMeasureIntervalViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "measureInterval", description: "in seconds")] 33 | 34 | operatorInfo = "Measures and emits the time interval between events received from an upstream publisher." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let measureInterval = subject 40 | .measureInterval(using: DispatchQueue.main) 41 | 42 | measureInterval 43 | .sink( 44 | receiveValue: { value in 45 | let seconds = String(format: "%.1f", Double(value.magnitude) / pow(10, 9)) 46 | display(seconds) 47 | } 48 | ) 49 | """ 50 | } 51 | 52 | override func setupBindings() { 53 | let subject1 = Subject( 54 | title: "Subject", 55 | inputValues: ["🌟", "✅", nil] 56 | ) 57 | 58 | subjects = [subject1] 59 | 60 | let measureInterval = subject1.trackedPublisher! 61 | .measureInterval(using: DispatchQueue.main) 62 | 63 | subscription = measureInterval 64 | .handleEvents( 65 | receiveSubscription: { [weak self] _ in 66 | guard let self = self else { return } 67 | self.updateOperator(.subscription) 68 | }, receiveCancel: { [weak self] in 69 | guard let self = self else { return } 70 | self.updateOperator(.cancel) 71 | }, receiveRequest: { [weak self] _ in 72 | guard let self = self else { return } 73 | self.updateOperator(.request) 74 | } 75 | ) 76 | .sink( 77 | receiveCompletion: { [weak self] completion in 78 | guard let self = self else { return } 79 | self.setCompletionFromOperator(completion: completion) 80 | }, receiveValue: { [weak self] value in 81 | guard let self = self else { return } 82 | 83 | let seconds = String(format: "%.1f", Double(value.magnitude) / pow(10, 9)) 84 | self.setValueFromOperator(value: "\(seconds)s") 85 | 86 | self.startCountdown() 87 | } 88 | ) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Timing/OperatorTimeoutViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorTimeoutViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorTimeoutViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | subjectOutputCountdown = 5 33 | countdownFinishKillsPublisher = true 34 | 35 | operators = [Operator(name: "timeout", description: "5 seconds")] 36 | 37 | operatorInfo = "Terminates publishing if the upstream publisher exceeds the specified time interval without producing an element." 38 | 39 | operatorCode = """ 40 | let subject = PassthroughSubject() 41 | 42 | let timeout = subject 43 | .timeout(5, scheduler: DispatchQueue.main) 44 | 45 | timeout 46 | .sink( 47 | receiveValue: { value in 48 | display(value) 49 | } 50 | ) 51 | """ 52 | } 53 | 54 | override func setupBindings() { 55 | let subject1 = Subject( 56 | title: "Subject", 57 | inputValues: ["🌟", "✅", nil] 58 | ) 59 | 60 | subjects = [subject1] 61 | 62 | let timeout = subject1.trackedPublisher! 63 | .timeout(5, scheduler: DispatchQueue.main) 64 | 65 | subscription = timeout 66 | .handleEvents( 67 | receiveSubscription: { [weak self] _ in 68 | guard let self = self else { return } 69 | self.updateOperator(.subscription) 70 | }, receiveCancel: { [weak self] in 71 | guard let self = self else { return } 72 | self.updateOperator(.cancel) 73 | }, receiveRequest: { [weak self] _ in 74 | guard let self = self else { return } 75 | self.updateOperator(.request) 76 | } 77 | ) 78 | .sink( 79 | receiveCompletion: { [weak self] completion in 80 | guard let self = self else { return } 81 | self.setCompletionFromOperator(completion: completion) 82 | }, receiveValue: { [weak self] value in 83 | guard let self = self else { return } 84 | self.setValueFromOperator(value: value) 85 | } 86 | ) 87 | 88 | startCountdown() 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Transforming/OperatorCollectViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorCollectViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | public class OperatorCollectViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "collect", description: "count: 3")] 33 | 34 | operatorInfo = "Collects up to the specified number of elements, and then emits a single array of the collection." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let collect = subject.collect(3) 40 | 41 | collect 42 | .sink( 43 | receiveValue: { values in 44 | let mappedValues = values.map { $0 ?? "nil" } 45 | display(mappedValues.joined(separator: " ")) 46 | } 47 | ) 48 | """ 49 | } 50 | 51 | override func setupBindings() { 52 | let subject = Subject( 53 | title: "Subject", 54 | inputValues: ["🌟", "✅", nil] 55 | ) 56 | 57 | subjects = [subject] 58 | 59 | let collect = subject.trackedPublisher!.collect(3) 60 | 61 | subscription = collect 62 | .handleEvents( 63 | receiveSubscription: { [weak self] _ in 64 | guard let self = self else { return } 65 | self.updateOperator(.subscription) 66 | }, receiveCancel: { [weak self] in 67 | guard let self = self else { return } 68 | self.updateOperator(.cancel) 69 | }, receiveRequest: { [weak self] _ in 70 | guard let self = self else { return } 71 | self.updateOperator(.request) 72 | } 73 | ) 74 | .sink( 75 | receiveCompletion: { [weak self] completion in 76 | guard let self = self else { return } 77 | self.setCompletionFromOperator(completion: completion) 78 | }, receiveValue: { [weak self] values in 79 | guard let self = self else { return } 80 | let mappedValues = values.map { $0 ?? "nil" } 81 | self.setValueFromOperator(value: mappedValues.joined(separator: " ")) 82 | } 83 | ) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Transforming/OperatorMapViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorMapViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorMapViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "map", description: "value + 🍏")] 33 | 34 | operatorInfo = "Transforms all elements from the upstream publisher with a provided closure." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let map = subject 40 | .map { value -> String? in 41 | return "\\(value ?? "nil") 🍏" 42 | } 43 | 44 | map 45 | .sink( 46 | receiveValue: { value in 47 | display(value) 48 | } 49 | ) 50 | """ 51 | } 52 | 53 | override func setupBindings() { 54 | let subject = Subject( 55 | title: "Subject", 56 | inputValues: ["🌟", "✅", nil] 57 | ) 58 | 59 | subjects = [subject] 60 | 61 | let map = subject.trackedPublisher! 62 | .map { value -> String? in 63 | return "\(value ?? "nil") 🍏" 64 | } 65 | 66 | subscription = map 67 | .handleEvents( 68 | receiveSubscription: { [weak self] _ in 69 | guard let self = self else { return } 70 | self.updateOperator(.subscription) 71 | }, receiveCancel: { [weak self] in 72 | guard let self = self else { return } 73 | self.updateOperator(.cancel) 74 | }, receiveRequest: { [weak self] _ in 75 | guard let self = self else { return } 76 | self.updateOperator(.request) 77 | } 78 | ) 79 | .sink( 80 | receiveCompletion: { [weak self] completion in 81 | guard let self = self else { return } 82 | self.setCompletionFromOperator(completion: completion) 83 | }, receiveValue: { [weak self] value in 84 | guard let self = self else { return } 85 | self.setValueFromOperator(value: value) 86 | } 87 | ) 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Transforming/OperatorReplaceEmptyViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorReplaceEmptyViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorReplaceEmptyViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "replaceEmpty", description: "to 🙈")] 33 | 34 | operatorInfo = "Replaces an empty stream with the provided element. Try tapping `Finish` immediately without sending any values." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let replaceEmpty = subject 40 | .replaceEmpty(with: "🙈") 41 | 42 | replaceEmpty 43 | .sink( 44 | receiveValue: { value in 45 | display(value) 46 | } 47 | ) 48 | """ 49 | } 50 | 51 | override func setupBindings() { 52 | let subject = Subject( 53 | title: "Subject", 54 | inputValues: ["🌟", "✅", nil] 55 | ) 56 | 57 | subjects = [subject] 58 | 59 | let replaceEmpty = subject.trackedPublisher! 60 | .replaceEmpty(with: "🙈") 61 | 62 | subscription = replaceEmpty 63 | .handleEvents( 64 | receiveSubscription: { [weak self] _ in 65 | guard let self = self else { return } 66 | self.updateOperator(.subscription) 67 | }, receiveCancel: { [weak self] in 68 | guard let self = self else { return } 69 | self.updateOperator(.cancel) 70 | }, receiveRequest: { [weak self] _ in 71 | guard let self = self else { return } 72 | self.updateOperator(.request) 73 | } 74 | ) 75 | .sink( 76 | receiveCompletion: { [weak self] completion in 77 | guard let self = self else { return } 78 | self.setCompletionFromOperator(completion: completion) 79 | }, receiveValue: { [weak self] value in 80 | guard let self = self else { return } 81 | self.setValueFromOperator(value: value) 82 | } 83 | ) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/OperatorViewControllers/Transforming/OperatorReplaceNilViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatorReplaceNilViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | import Combine 28 | 29 | class OperatorReplaceNilViewController: BaseOperatorViewController { 30 | 31 | override func setupOperator() { 32 | operators = [Operator(name: "replaceNil", description: "to 🐰")] 33 | 34 | operatorInfo = "Replaces nil elements in the stream with the provided element." 35 | 36 | operatorCode = """ 37 | let subject = PassthroughSubject() 38 | 39 | let replaceNil = subject 40 | .replaceNil(with: "🐰") 41 | 42 | replaceNil 43 | .sink( 44 | receiveValue: { value in 45 | display(value) 46 | } 47 | ) 48 | """ 49 | } 50 | 51 | override func setupBindings() { 52 | 53 | let subject = Subject( 54 | title: "Subject", 55 | inputValues: ["🌟", "✅", nil] 56 | ) 57 | 58 | subjects = [subject] 59 | 60 | let replaced = subject.trackedPublisher! 61 | .replaceNil(with: "🐰") 62 | 63 | subscription = replaced 64 | .handleEvents( 65 | receiveSubscription: { [weak self] _ in 66 | guard let self = self else { return } 67 | self.updateOperator(.subscription) 68 | }, receiveCancel: { [weak self] in 69 | guard let self = self else { return } 70 | self.updateOperator(.cancel) 71 | }, receiveRequest: { [weak self] _ in 72 | guard let self = self else { return } 73 | self.updateOperator(.request) 74 | } 75 | ) 76 | .sink( 77 | receiveCompletion: { [weak self] completion in 78 | guard let self = self else { return } 79 | self.setCompletionFromOperator(completion: completion) 80 | }, receiveValue: { [weak self] value in 81 | guard let self = self else { return } 82 | self.setValueFromOperator(value: value) 83 | } 84 | ) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/ViewControllers/SymbolsTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SymbolsTableViewController.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | 28 | open class SymbolsTableViewController: UITableViewController { 29 | 30 | let imageNames = ["link", "square.and.arrow.up", "xmark.circle", "exclamationmark.triangle", "checkmark"] 31 | let symbolTitles = ["Received subscription", "Received demand", "Cancelled", "Completed with error", "Completed"] 32 | 33 | open override func viewDidLoad() { 34 | super.viewDidLoad() 35 | 36 | title = "Publisher Symbol Guide" 37 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(performDismiss)) 38 | 39 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: "SymbolCell") 40 | tableView.allowsSelection = false 41 | } 42 | 43 | public override func numberOfSections(in tableView: UITableView) -> Int { 44 | return 1 45 | } 46 | 47 | public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 48 | return 6 49 | } 50 | 51 | public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 52 | let cell = tableView.dequeueReusableCell(withIdentifier: "SymbolCell", for: indexPath) 53 | 54 | if indexPath.row == 0 { 55 | cell.imageView?.image = Assets.numberOfSubscriptions.image 56 | cell.textLabel?.text = "Number of subscriptions" 57 | } else { 58 | cell.imageView?.image = UIImage(systemName: imageNames[indexPath.row - 1]) 59 | cell.textLabel?.text = symbolTitles[indexPath.row - 1] 60 | } 61 | 62 | return cell 63 | } 64 | 65 | @objc private func performDismiss() { 66 | dismiss(animated: true, completion: nil) 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Sources/CombineOperatorsCore/Views/InputButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InputButton.swift 3 | // CombineOperatorsCore 4 | // 5 | // Copyright (c) 2020 cocoatoucher user on github.com (https://github.com/cocoatoucher/) 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | import UIKit 27 | 28 | class InputButton: UIButton { 29 | 30 | enum Mode { 31 | case value 32 | case error 33 | case finish 34 | } 35 | 36 | override var isEnabled: Bool { 37 | didSet { 38 | alpha = isEnabled ? 1.0 : 0.3 39 | } 40 | } 41 | 42 | override var isHighlighted: Bool { 43 | didSet { 44 | if isHighlighted { 45 | backgroundColor = UIColor( 46 | named: "\(colorNamePrefix)Highlighted", 47 | in: Bundle.module, 48 | compatibleWith: nil 49 | ) 50 | } else { 51 | backgroundColor = UIColor( 52 | named: colorNamePrefix, 53 | in: Bundle.module, 54 | compatibleWith: nil 55 | ) 56 | } 57 | } 58 | } 59 | 60 | let colorNamePrefix: String 61 | 62 | init(mode: Mode = .value) { 63 | switch mode { 64 | case .value: 65 | colorNamePrefix = "inputButton" 66 | case .error: 67 | colorNamePrefix = "errorButton" 68 | case .finish: 69 | colorNamePrefix = "finishButton" 70 | } 71 | 72 | super.init(frame: .zero) 73 | 74 | setTitleColor(.black, for: .normal) 75 | backgroundColor = UIColor( 76 | named: colorNamePrefix, 77 | in: Bundle.module, 78 | compatibleWith: nil 79 | ) 80 | layer.cornerRadius = 4.0 81 | titleLabel?.font = UIFont.boldSystemFont(ofSize: 12.0) 82 | } 83 | 84 | @available(*, unavailable) 85 | required init?(coder: NSCoder) { 86 | fatalError("init(coder:) has not been implemented") 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /Tests/CombineOperatorsCoreTests/CombineOperatorsCoreTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import CombineOperatorsCore 3 | 4 | final class CombineOperatorsCoreTests: 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(CombineOperatorsCore().text, "Hello, World!") 10 | } 11 | 12 | static var allTests = [ 13 | ("testExample", testExample), 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /Tests/CombineOperatorsCoreTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(CombineOperatorsCoreTests.allTests), 7 | ] 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import CombineOperatorsCoreTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += CombineOperatorsCoreTests.allTests() 7 | XCTMain(tests) 8 | -------------------------------------------------------------------------------- /swiftgen.yml: -------------------------------------------------------------------------------- 1 | xcassets: 2 | - inputs: Sources/CombineOperatorsCore/Resources/Media.xcassets 3 | outputs: 4 | templateName: swift4 5 | output: Sources/CombineOperatorsCore/Resources/SwiftGen/Assets.swift 6 | params: 7 | enumName: Assets 8 | publicAccess: true 9 | --------------------------------------------------------------------------------