├── .gitignore
├── Previews
├── combine-zip.gif
└── swiftui-96x96_2x.png
├── README.md
├── combine-extensions
├── CombineExtensions.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── CombineExtensions
│ ├── CombineExtensions.h
│ ├── CombineExtensions.swift
│ ├── Info.plist
│ └── UnwrapPublisher.swift
└── CombineExtensionsTests
│ ├── CombineExtensionsTests.swift
│ └── Info.plist
├── combine-playground
├── combine-playground.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── combine-playground
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── ContentView
│ ├── ContentView.swift
│ └── ContentViewModel.swift
│ ├── Data
│ ├── DataModels.swift
│ ├── DataService.swift
│ ├── StreamStore.swift
│ └── UserDefaultWrapper.swift
│ ├── Extensions
│ ├── ModelExtensions.swift
│ └── OperatorExtensions.swift
│ ├── Info.plist
│ ├── Menu
│ └── MenuRow.swift
│ ├── MultiBallViews
│ ├── MultiBallTunnelView.swift
│ ├── MultiBallView.swift
│ └── MultiBallViewModel.swift
│ ├── MultiStreamViews
│ ├── MultiStreamView.swift
│ ├── MultiStreamViewModel.swift
│ ├── MultiValueStreamView.swift
│ └── UpdatableStreamViewModel.swift
│ ├── PlaygroundStreamView
│ ├── PlaygroundStreamView.swift
│ └── PlaygroundStreamViewModel.swift
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── SceneDelegate.swift
│ ├── SingleBallViews
│ ├── BallTunnelView.swift
│ ├── CircularTextView.swift
│ └── CircularTextViewModel.swift
│ ├── SingleStreamView
│ ├── DataStreamViewModel.swift
│ ├── SingleStreamView.swift
│ └── StreamViewModel.swift
│ ├── StreamListViews
│ ├── JoinOperationListStreamView.swift
│ ├── NewStreamView
│ │ ├── NewStreamView.swift
│ │ └── NewStreamViewModel.swift
│ ├── OperationStreamListView.swift
│ ├── StreamListView.swift
│ └── UnifyingOperationListStreamView.swift
│ ├── Styles
│ ├── ButtonModifier.swift
│ └── CombineDemoButton.swift
│ ├── UpdateJoinStreamView
│ ├── UpdateJoinStreamView.swift
│ └── UpdateJoinStreamViewModel.swift
│ ├── UpdateOperationStreamView
│ ├── UpdateOperationStreamView.swift
│ └── UpdateOperationStreamViewModel.swift
│ ├── UpdateStreamView
│ ├── UpdateStreamView.swift
│ └── UpdateStreamViewModel.swift
│ └── UpdateUnifyingStreamView
│ ├── UpdateUnifyingStreamView.swift
│ └── UpdateUnifyingStreamViewModel.swift
└── tutorials
└── combine-tutorial
├── chapter1
├── CombineTutorial.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── CombineTutorial
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── CircularTextView.swift
│ ├── CombineStreamView.swift
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── SceneDelegate.swift
│ ├── StreamView.swift
│ └── TunnelView.swift
├── chapter2
├── CombineTutorial.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── CombineTutorial
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── CircularTextView.swift
│ ├── CombineMapStreamView.swift
│ ├── CombineScanStreamView.swift
│ ├── CombineStreamView.swift
│ ├── ContentView.swift
│ ├── GenericCombineStreamView.swift
│ ├── Info.plist
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── SceneDelegate.swift
│ ├── StreamView.swift
│ └── TunnelView.swift
├── chapter3
├── CombineTutorial.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── CombineTutorial
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── CircularTextView.swift
│ ├── CombineMapStreamView.swift
│ ├── CombineScanStreamView.swift
│ ├── CombineStreamView.swift
│ ├── ContentView.swift
│ ├── DoublePublisherStreamView.swift
│ ├── GenericCombineStreamView.swift
│ ├── Info.plist
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── SceneDelegate.swift
│ ├── StreamView.swift
│ └── TunnelView.swift
├── chapter4
├── CombineTutorial.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── CombineTutorial
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── CircularTextArrayView.swift
│ ├── CircularTextView.swift
│ ├── CombineMapStreamView.swift
│ ├── CombineScanStreamView.swift
│ ├── CombineStreamView.swift
│ ├── ContentView.swift
│ ├── DoublePublisherStreamView.swift
│ ├── GenericCombineStreamView.swift
│ ├── Info.plist
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── SceneDelegate.swift
│ ├── StreamView.swift
│ └── TunnelView.swift
└── chapter5
├── CombineTutorial.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
└── CombineTutorial
├── AppDelegate.swift
├── Assets.xcassets
├── AppIcon.appiconset
│ └── Contents.json
└── Contents.json
├── Base.lproj
└── LaunchScreen.storyboard
├── ButtonModifier.swift
├── CircularTextArrayView.swift
├── CircularTextView.swift
├── CombineMapStreamView.swift
├── CombineScanStreamView.swift
├── CombineStreamView.swift
├── ContentView.swift
├── DescriptiveTunnelView.swift
├── DoublePublisherStreamView.swift
├── GenericCombineStreamView.swift
├── Info.plist
├── MultiCircularTextView.swift
├── Preview Content
└── Preview Assets.xcassets
│ └── Contents.json
├── SceneDelegate.swift
├── StreamView.swift
└── TunnelView.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | # Package.resolved
41 | .build/
42 |
43 | # CocoaPods
44 | #
45 | # We recommend against adding the Pods directory to your .gitignore. However
46 | # you should judge for yourself, the pros and cons are mentioned at:
47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
48 | #
49 | # Pods/
50 | #
51 | # Add this line if you want to avoid checking in source code from the Xcode workspace
52 | # *.xcworkspace
53 |
54 | # Carthage
55 | #
56 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
57 | # Carthage/Checkouts
58 |
59 | Carthage/Build
60 |
61 | # Accio dependency management
62 | Dependencies/
63 | .accio/
64 |
65 | # fastlane
66 | #
67 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
68 | # screenshots whenever they are needed.
69 | # For more information about the recommended setup visit:
70 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
71 |
72 | fastlane/report.xml
73 | fastlane/Preview.html
74 | fastlane/screenshots/**/*.png
75 | fastlane/test_output
76 |
77 | # Code Injection
78 | #
79 | # After new code Injection tools there's a generated folder /iOSInjectionProject
80 | # https://github.com/johnno1962/injectionforxcode
81 |
82 | iOSInjectionProject/
83 | .DS_Store
84 |
--------------------------------------------------------------------------------
/Previews/combine-zip.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevinjohnason/combine-magic-swiftui/6b4c9be67fcc423e4cfab1abf34512de3d4640b4/Previews/combine-zip.gif
--------------------------------------------------------------------------------
/Previews/swiftui-96x96_2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevinjohnason/combine-magic-swiftui/6b4c9be67fcc423e4cfab1abf34512de3d4640b4/Previews/swiftui-96x96_2x.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## `Combine Magic with SwiftUI`
2 |
3 | 
4 |
5 | ### About
6 |
7 | Visual examples using *SwiftUI* to demonstrate the behaviors of *Combine*.
8 |
9 | ### Combine Playground
10 | [SwiftUI App Source Code](combine-playground)
11 |
12 | ### Medium Series
13 | 1. Visualize Combine Magic with SwiftUI Series
14 | 1. [Create your Combine Playground in SwiftUI](https://medium.com/@kevinminority/visualize-combine-magic-with-swiftui-part-1-3a56e2a461b3)
15 | - [Source Code](tutorials/combine-tutorial/chapter1)
16 | 2. [Operators, subscribing, and canceling in Combine](https://medium.com/@kevinminority/visualize-combine-magic-with-swiftui-part-2-2c613370388b)
17 | - [Source Code](tutorials/combine-tutorial/chapter2)
18 | 3. [Merge and Append in Action](https://medium.com/@kevinminority/visualize-combine-magic-with-swiftui-part-3-a3f0cc42bcc8)
19 | - [Source Code](tutorials/combine-tutorial/chapter3)
20 | 4. [What are the differences between Zip and CombineLatest](https://medium.com/@kevinminority/visualize-combine-magic-with-swiftui-part-4-6d0c5678f89e)
21 | - [Source Code](tutorials/combine-tutorial/chapter4)
22 | 5. [SwiftUI ViewModifier, Animation, and Transition](https://medium.com/flawless-app-stories/visualize-combine-magic-with-swiftui-part-5-2783adddbd1d)
23 | - [Source Code](tutorials/combine-tutorial/chapter5)
24 | 2. Persist and Distribute Logic with Swift Combine
25 | 1. [Persist Business Logic With Swift Combine](https://medium.com/better-programming/persist-business-logic-with-swift-combine-519efb3a7e37)
26 | - [Source Code](combine-playground)
27 | 2. [Persist Filtering Logic With Swift Combine](https://medium.com/better-programming/persist-filtering-logics-with-swift-combine-6c3594be77cc)
28 | - [Source Code](combine-playground)
29 |
--------------------------------------------------------------------------------
/combine-extensions/CombineExtensions.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/combine-extensions/CombineExtensions.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/combine-extensions/CombineExtensions/CombineExtensions.h:
--------------------------------------------------------------------------------
1 | //
2 | // CombineExtensions.h
3 | // CombineExtensions
4 | //
5 | // Created by kevin.cheng on 11/20/19.
6 | // Copyright © 2019 Kevin-Cheng. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for CombineExtensions.
12 | FOUNDATION_EXPORT double CombineExtensionsVersionNumber;
13 |
14 | //! Project version string for CombineExtensions.
15 | FOUNDATION_EXPORT const unsigned char CombineExtensionsVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/combine-extensions/CombineExtensions/CombineExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CombineExtensions.swift
3 | // combine-extensions
4 | //
5 | // Created by kevin.cheng on 11/20/19.
6 | // Copyright © 2019 Kevin-Cheng. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Combine
11 |
12 | public extension Set where Element == AnyCancellable {
13 | mutating func cancelAll() {
14 | self.forEach {
15 | $0.cancel()
16 | }
17 | self.removeAll()
18 | }
19 | }
20 |
21 | public typealias CancellableSet = Set
22 |
--------------------------------------------------------------------------------
/combine-extensions/CombineExtensions/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 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/combine-extensions/CombineExtensions/UnwrapPublisher.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UnwrapPublisher.swift
3 | // CombineExtensions
4 | //
5 | // Created by Kevin Cheng on 12/27/19.
6 | // Copyright © 2019 Kevin-Cheng. All rights reserved.
7 | //
8 |
9 | import Combine
10 |
11 | public extension Publishers {
12 |
13 | struct Unwrap : Publisher where Upstream : Publisher, Upstream.Output == Optional