> {
36 | let (i, _) = Signal.create()
37 | return map { value in Callback(value, i) }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Tests/CwlViewsCatalog_macOS/Sources/WebView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebView.swift
3 | // CwlViewsCatalog_iOS
4 | //
5 | // Created by Matt Gallagher on 10/2/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import CwlViews
21 |
22 | struct WebViewState: CodableContainer {
23 | init() {
24 | }
25 | }
26 |
27 | func webView(_ webViewState: WebViewState) -> ViewConvertible {
28 | return View(
29 | .layer -- Layer(.backgroundColor -- NSColor.darkGray.cgColor),
30 | .layout -- Layout.fill(
31 | WebKitView.scrollEmbedded(
32 | .minimumFontSize -- 12,
33 | .loadHTMLString <-- Signal<(string: String, baseURL: URL?)>.just((.htmlString, nil)).ignoreCallback()
34 | )
35 | )
36 | )
37 | }
38 |
39 | private extension String {
40 | static let htmlString = """
41 |
42 |
43 |
44 |
45 |
46 | Hello, there!
47 | This text is loaded in a WKWebView.
48 |
49 |
50 | """
51 | }
52 |
--------------------------------------------------------------------------------
/Sources/CwlViews/Core/Adapter/CwlToggleVar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlToggleVar.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 3/1/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | public struct ToggleValue: PersistentAdapterState {
21 | public typealias Message = Void
22 | public typealias Notification = Bool
23 |
24 | public let value: Bool
25 | public init(value: Bool) {
26 | self.value = value
27 | }
28 |
29 | public func reduce(message: Void, feedback: SignalMultiInput) -> Output {
30 | return Output(state: ToggleValue(value: !value), notification: !value)
31 | }
32 |
33 | public func resume() -> Notification? { return value }
34 |
35 | public static func initialize(message: Message, feedback: SignalMultiInput) -> Output? {
36 | return nil
37 | }
38 | }
39 |
40 | public typealias ToggleVar = Adapter
41 |
42 | public extension Adapter where State == ToggleValue {
43 | init(_ value: Bool) {
44 | self.init(adapterState: ToggleValue(value: value))
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Tests/UnitTests/macOS/CwlViewsUnitTestHarness_macOS/main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // main.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 4/1/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import CwlViews
21 |
22 | fileprivate let executableName = (Bundle.main.localizedInfoDictionary?[kCFBundleNameKey as String] as? String) ?? (Bundle.main.localizedInfoDictionary?[kCFBundleExecutableKey as String] as? String) ?? ProcessInfo.processInfo.processName
23 |
24 | applicationMain(type: TestApplication.self) {
25 | Application(
26 | .mainMenu -- Menu(
27 | .title -- "Main Menu",
28 | .items -- [
29 | MenuItem(.submenu -- Menu(
30 | .systemName -- .apple,
31 | .title -- executableName,
32 | .items -- [
33 | MenuItem(
34 | .title -- String(format: NSLocalizedString("Quit %@", tableName: "MainMenu", comment: ""), executableName),
35 | .action --> #selector(NSApplication.terminate(_:)),
36 | .keyEquivalent -- "q"
37 | )
38 | ]
39 | ))
40 | ]
41 | )
42 | )
43 | }
44 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/Sources/CwlUtils/CwlFew.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlFew.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 29/1/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import Foundation
21 |
22 | public enum Few {
23 | case none
24 | case single(T)
25 | case array(Array)
26 | }
27 |
28 | extension Few: Collection {
29 | public func index(after i: Int) -> Int {
30 | return i + 1
31 | }
32 |
33 | public var count: Int {
34 | switch self {
35 | case .none: return 0
36 | case .single: return 1
37 | case .array(let a): return a.count
38 | }
39 | }
40 |
41 | public var startIndex: Int {
42 | return 0
43 | }
44 |
45 | public var endIndex: Int {
46 | switch self {
47 | case .none: return 0
48 | case .single: return 1
49 | case .array(let a): return a.endIndex
50 | }
51 | }
52 |
53 | public subscript(key: Int) -> T {
54 | switch self {
55 | case .none: fatalError()
56 | case .single(let value): return value
57 | case .array(let a): return a[key]
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/Tests/CwlUtils_iOSTestApp/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 |
27 |
28 |
--------------------------------------------------------------------------------
/Tests/CwlViewsCatalog_iOS/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 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Sources/CwlViewsTesting/macOS/CwlTableHeaderViewTesting_macOS.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlTableHeaderView_macOS.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 29/10/2015.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | #if os(macOS)
21 |
22 | extension BindingParser where Downcast: TableHeaderViewBinding {
23 | // You can easily convert the `Binding` cases to `BindingParser` using the following Xcode-style regex:
24 | // Replace: case ([^\(]+)\((.+)\)$
25 | // With: public static var $1: BindingParser<$2, TableHeaderView.Binding, Downcast> { return .init(extract: { if case .$1(let x) = \$0 { return x } else { return nil } }, upcast: { \$0.asTableHeaderViewBinding() }) }
26 |
27 | // 0. Static bindings are applied at construction and are subsequently immutable.
28 |
29 | // 1. Value bindings may be applied at construction and may subsequently change.
30 |
31 | // 2. Signal bindings are performed on the object after construction.
32 |
33 | // 3. Action bindings are triggered by the object after construction.
34 |
35 | // 4. Delegate bindings require synchronous evaluation within the object's context.
36 | }
37 |
38 | #endif
39 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/CwlSignal.playground/Pages/Play area.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 |
3 | # Experiment with CwlSignal
4 |
5 | > **This playground requires the CwlSignal.framework built by the CwlSignal_macOS scheme.** If you're seeing errors finding or building module 'CwlSignal', follow the Build Instructions on the [Contents](Contents) page.
6 |
7 | This page contains a couple quick and simple examples you can play with to try and understand some of the basics of CwlSignal.
8 |
9 | */
10 | import CwlSignal
11 |
12 | print("Prints ghost and pumpkin on their own lines, then stops:")
13 |
14 | // A lazily generated sequence of strings that feeds into `subscribeValuesWhile`, a subscribe function that manages the output internally (which is convenient in unscoped locations like playgrounds where there's no context in which to store the output).
15 | Signal.generate { input in
16 | if let i = input {
17 | i.send("👻", "🎃", "👹", "😈")
18 | }
19 | }.subscribeValuesWhile {
20 | print($0)
21 |
22 | // Stop immediately after the pumpkin"
23 | return $0 == "🎃" ? false : true
24 | }
25 |
26 | print("Aggregates four smileys into single string:")
27 |
28 | // `Signal.just(_:...)` creates a signal using the provided values and `toSequence` offers synchronous conversion back to a Swift `Sequence` type. The `aggregate` operator turns a signal of many values into a signal of one value (in this case, by concatenating the strings). The `next()` function is the Swift Standard Library Sequence function – it gets the only value in the sequence after the `aggregate` operator collapsed the four smileys down to a single string.
29 | let aggregated = Signal
30 | .just("😀", "🙃", "😉", "🤣")
31 | .aggregate("Start: ") { return $0 + $1 }
32 | .toSequence()
33 | .next()!
34 | print(aggregated)
35 |
36 | /*:
37 | ---
38 |
39 | *This example writes to the "Debug Area". If it is not visible, show it from the menubar: "View" → "Debug Area" → "Show Debug Area".*
40 |
41 | */
42 |
43 |
--------------------------------------------------------------------------------
/Tests/CwlViewsCatalog_iOS/Sources/WebView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebView.swift
3 | // CwlViewsCatalog_iOS
4 | //
5 | // Created by Matt Gallagher on 10/2/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import CwlViews
21 |
22 | struct WebViewState: CodableContainer {
23 | init() {
24 | }
25 | }
26 |
27 | func webView(_ webViewState: WebViewState, _ navigationItem: NavigationItem) -> ViewControllerConvertible {
28 | return ViewController(
29 | .navigationItem -- navigationItem,
30 | .view -- View(
31 | .layoutMargins -- UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20),
32 | .backgroundColor -- .darkGray,
33 | .layout -- .fill(
34 | marginEdges: .allLayout,
35 | WebView(
36 | .loadHTMLString <-- Signal<(string: String, baseURL: URL?)>.just((.htmlString, nil)).ignoreCallback()
37 | )
38 | )
39 | )
40 | )
41 | }
42 |
43 | private extension String {
44 | static let htmlString = """
45 |
46 |
47 |
48 |
49 |
50 | Hello, there!
51 | This text is loaded in a WKWebView.
52 |
53 |
54 | """
55 | }
56 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/CwlSignal.playground/Pages/Advanced behaviors - lazy generation.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | # Advanced behaviors 2
3 |
4 | > **This playground requires the CwlSignal.framework built by the CwlSignal_macOS scheme.** If you're seeing errors finding or building module 'CwlSignal', follow the Build Instructions on the [Contents](Contents) page.
5 |
6 | ## Lazy generation
7 |
8 | In many cases, we want to defer the actual generation of values until *after* a subscriber is ready to receive them. In CwlSignal, we can use the `generate` function, which invokes its closure every time the "activation" state changes, to start after the signal graph becomes active.
9 |
10 | The `generate` function's closure will also be invoke with a `nil` value when the signal graph *deactives* so you can clean up resources.
11 |
12 | ---
13 | */
14 | import CwlSignal
15 |
16 | // Create an output immediately but only start creating data to feed into the pipeline after a listener connects.
17 | let signal = Signal.generate { input in
18 | if let i = input {
19 | print("Signal has activated")
20 | i.send(1, 2, 3)
21 | } else {
22 | print("Signal has deactivated")
23 | }
24 | }
25 |
26 | print("We're just about to subscribe.")
27 |
28 | // Subscribe to listen to the values output by the channel
29 | let output = signal.subscribe { result in
30 | switch result {
31 | case .success(let value): print("Value: \(value)")
32 | case .failure(let error): print("End of signal: \(error)")
33 | }
34 | }
35 |
36 | // SOMETHING TO TRY: replace the `generate` with `retainedGenerate` and the `input` will be automatically held until all outputs are cancelled.
37 |
38 | print("Done")
39 |
40 | /*:
41 | ---
42 |
43 | *This example writes to the "Debug Area". If it is not visible, show it from the menubar: "View" → "Debug Area" → "Show Debug Area".*
44 |
45 | [Next page: Advanced behaviors - capturing](@next)
46 |
47 | [Previous page: Advanced behaviors - continuous](@previous)
48 | */
49 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/CwlSignal.playground/Pages/Serial pipelines - channel.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | # Serial pipelines 3: channel
3 |
4 | > **This playground requires the CwlSignal.framework built by the CwlSignal_macOS scheme.** If you're seeing the error: "no such module 'CwlSignal'" follow the Build Instructions on the [Contents](Contents) page.
5 |
6 | ## Nicer syntax for pipelines
7 |
8 | Programming with `Signal` involves building lots of little pipelines which we've called "channels" – since they model a structure into which you can send values that will be communicated through to the output. However, the loose tuple of `SignalInput` and `SignalChannel` that `Signal.create()` returns is a clumsy way of manipulating this channel, requiring separate holding and manipulation of both ends.
9 |
10 | The `Signal.channel()` function returns a `SignalChannel`. This `SignalChannel` wraps the same `SignalInput` and `Signal` that would be returned from `Signal.create()` but you can apply transformations directly to the `SignalChannel` and they are applied to the `Signal` half, returning another `SignalChannel` wrapping the old input and new signal or other result from the transform. This lets you construct a signal pipeline with multiple stages in a single, linear expression.
11 |
12 | Here's the example from the previous page, using `Signal.channel()` instead of `Signal.create()`.
13 |
14 | ---
15 | */
16 | import CwlSignal
17 |
18 | // On the previous page, this line required two separate lines and an otherwise unusued `signal` declaration.
19 | let input = Signal.channel().map { $0 * 2 }.subscribeValuesUntilEnd { print("Value received: \($0)") }
20 |
21 | // Send values to the input end
22 | input.send(1, 2, 3)
23 | input.complete()
24 |
25 | /*:
26 | ---
27 |
28 | *This example writes to the "Debug Area". If it is not visible, show it from the menubar: "View" → "Debug Area" → "Show Debug Area".*
29 |
30 | [Next page: Parallel composition - asynchronous](@next)
31 |
32 | [Previous page: Serial pipelines - map](@previous)
33 | */
34 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/CwlSignal.playground/Pages/Serial pipelines - transform.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 |
3 | # Serial pipelines 1: transform
4 |
5 | > **This playground requires the CwlSignal.framework built by the CwlSignal_macOS scheme.** If you're seeing the error: "no such module 'CwlSignal'" follow the Build Instructions on the [Contents](Contents) page.
6 |
7 | ## The `transform` function
8 |
9 | The previous example merely passed values from the input through to the output. The real strength of reactive programming starts when we add multiple stages to the channel that process values as they pass through.
10 |
11 | There are lots of different "operator" functions for chaining `Signal` instances together (including names like `map` and `compactMap` that you might recognize from `Sequence` and `Collection` processing in Swift) but most are implemented on top of the underlying `transform` function.
12 |
13 | In this example, we turn each `Int` value that passes through the channel into an equal number of `Beep` strings.
14 |
15 | ---
16 | */
17 | import CwlSignal
18 |
19 | let (input, signal) = Signal.create()
20 |
21 | // Transform into signal that emits the string "Beep", a number of times equal to the integer received
22 | let output = signal.transform { result in
23 | switch result {
24 | case .success(let v): return .values(sequence: (0..` automatically, ignoring errors (good if you don't need to know about end-of-stream conditions).
29 | print(value)
30 | }
31 |
32 | input.send(1)
33 | input.send(2)
34 | input.send(3)
35 | input.complete()
36 |
37 | /*:
38 | ---
39 |
40 | *This example writes to the "Debug Area". If it is not visible, show it from the menubar: "View" → "Debug Area" → "Show Debug Area".*
41 |
42 | [Next page: Serial pipelines - map](@next)
43 |
44 | [Previous page: Basic channel](@previous)
45 | */
46 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/Tests/CwlUtilsTests/CwlStackFrameTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlStackFrameTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/02/26.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | class StackFrameTests: XCTestCase {
26 | func testCallStackReturnAddresses() {
27 | var a = callStackReturnAddresses()
28 | a.remove(at: 0)
29 | var b = Thread.callStackReturnAddresses.map { $0.uintValue }
30 | b.remove(at: 0)
31 | XCTAssert(a == b)
32 |
33 | a = callStackReturnAddresses(skip: 2)
34 | b.remove(at: 0)
35 | XCTAssert(a == b)
36 |
37 | a = callStackReturnAddresses(skip: 2, maximumAddresses: 10)
38 | XCTAssert(a == Array(b[0..<10]))
39 | }
40 |
41 | func testNSThreadCallStackReturnAddressesPerformance() {
42 | measure {
43 | var traces = [[NSNumber]]()
44 | for _ in 0..<1000 {
45 | traces.append(Thread.callStackReturnAddresses)
46 | }
47 | }
48 | }
49 |
50 | func testCallStackReturnAddressesPerformance() {
51 | measure {
52 | var traces = [[UInt]]()
53 | for _ in 0..<1000 {
54 | traces.append(callStackReturnAddresses())
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Sources/CwlViews/Core/BindingValues/CwlTargetAction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlTargetAction.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 2017/03/23.
6 | // Copyright © 2017 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | /// This type encapsulates the idea that target-action pairs in Cocoa may target a specific object (by setting the target to non-nil) or may let the responder chain search for a responder that handles a specific selector.
21 | public enum TargetAction {
22 | case firstResponder(Selector)
23 | case singleTarget(SignalInput)
24 | }
25 |
26 | public protocol TargetActionSender: class {
27 | var action: Selector? { get set }
28 | var target: AnyObject? { get set }
29 | }
30 |
31 | extension TargetAction {
32 | public func apply(to instance: Source, constructTarget: () -> SignalActionTarget, selector: Selector = SignalActionTarget.selector) -> Lifetime? {
33 | switch self {
34 | case .firstResponder(let s):
35 | instance.target = nil
36 | instance.action = s
37 | return nil
38 | case .singleTarget(let s):
39 | let target = constructTarget()
40 | instance.target = target
41 | instance.action = SignalActionTarget.selector
42 | return target.signal.cancellableBind(to: s)
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Tests/CwlViewsCatalog_iOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/Sources/CwlViews/Core/BindingValues/CwlInitialSubsequent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlInitialSubsequent.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 3/1/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | public struct InitialSubsequent {
21 | public let initial: Value?
22 | public let subsequent: SignalCapture?
23 |
24 | init(signal: Interface) where Interface.OutputValue == Value {
25 | let capture = signal.capture()
26 | let values = capture.values
27 | self.init(initial: values.last, subsequent: capture)
28 | }
29 |
30 | init(initial: Value? = nil, subsequent: SignalCapture? = nil) {
31 | self.initial = initial
32 | self.subsequent = subsequent
33 | }
34 |
35 | public func resume() -> Signal? {
36 | return subsequent?.resume()
37 | }
38 |
39 | public func apply(_ instance: I, handler: @escaping (I, Value) -> Void) -> Lifetime? {
40 | return resume().flatMap { $0.apply(instance, handler: handler) }
41 | }
42 |
43 | public func apply(_ instance: I, _ storage: Storage, handler: @escaping (I, Storage, Value) -> Void) -> Lifetime? {
44 | return resume().flatMap { $0.apply(instance, storage, handler: handler) }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Sources/CwlViews/Core/Adapter/CwlTempVar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlTempVar.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 3/1/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | public struct TempValue: NonPersistentAdapterState {
21 | public typealias Message = Value
22 | public typealias Notification = Value
23 |
24 | let temporaryValue: Value?
25 | public init() {
26 | temporaryValue = nil
27 | }
28 |
29 | fileprivate init(temporaryValue: Value) {
30 | self.temporaryValue = temporaryValue
31 | }
32 |
33 | public func reduce(message: Value, feedback: SignalMultiInput) -> Output {
34 | return Output(state: TempValue(temporaryValue: message), notification: message)
35 | }
36 |
37 | public func resume() -> Notification? {
38 | return temporaryValue
39 | }
40 |
41 | public static func initialize(message: Message, feedback: SignalMultiInput) -> Output? {
42 | return Output(state: TempValue(temporaryValue: message), notification: message)
43 | }
44 | }
45 |
46 | public typealias TempVar = Adapter>
47 |
48 | public extension Adapter {
49 | init(_ value: Value) where TempValue == State {
50 | self.init(adapterState: TempValue(temporaryValue: value))
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Tests/UnitTests/iOS/CwlViewsUnitTestHarness_iOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/Sources/CwlViews/Core/Utilities/CwlImageDrawn.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlImageDrawn.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 5/2/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import Foundation
21 |
22 | #if os(macOS)
23 | extension NSImage {
24 | public static func drawn(width: CGFloat, height: CGFloat, flipped: Bool = true, _ function: @escaping (CGContext, CGRect) -> Void) -> NSImage {
25 | let size = CGSize(width: width, height: height)
26 | return NSImage(size: size, flipped: flipped) { rect -> Bool in
27 | guard let context = NSGraphicsContext.current else { return false }
28 | function(context.cgContext, rect)
29 | return true
30 | }
31 | }
32 | }
33 | #else
34 | extension UIImage {
35 | public static func drawn(width: CGFloat, height: CGFloat, _ function: (CGContext, CGRect) -> Void) -> UIImage {
36 | let size = CGSize(width: width, height: height)
37 | UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
38 | if let graphicsContext = UIGraphicsGetCurrentContext() {
39 | function(graphicsContext, CGRect(origin: .zero, size: size))
40 | }
41 | let rectangleImage = UIGraphicsGetImageFromCurrentImageContext()
42 | UIGraphicsEndImageContext()
43 | return rectangleImage ?? UIImage()
44 | }
45 | }
46 | #endif
47 |
--------------------------------------------------------------------------------
/Tests/CwlViewsCatalog_iOS/Sources/SwitchView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwitchView.swift
3 | // CwlViewsCatalog_iOS
4 | //
5 | // Created by Matt Gallagher on 10/2/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import CwlViews
21 |
22 | struct SwitchViewState: CodableContainer {
23 | let value: Var
24 | init() {
25 | value = Var(true)
26 | }
27 | }
28 |
29 | func switchView(_ switchViewState: SwitchViewState, _ navigationItem: NavigationItem) -> ViewControllerConvertible {
30 | return ViewController(
31 | .navigationItem -- navigationItem,
32 | .view -- View(
33 | .backgroundColor -- .white,
34 | .layout -- .center(
35 | .view(
36 | Label(
37 | .text <-- switchViewState.value.map { .localizedStringWithFormat(.valueFormat, $0 ? String.on : String.off) }
38 | )
39 | ),
40 | .space(),
41 | .view(
42 | Switch(
43 | .isOn <-- switchViewState.value.distinctUntilChanged().animate(),
44 | .action(.valueChanged, \.isOn) --> switchViewState.value
45 | )
46 | )
47 | )
48 | )
49 | )
50 | }
51 |
52 | private extension String {
53 | static let valueFormat = NSLocalizedString("Switch is %@", comment: "")
54 | static let on = NSLocalizedString("on", comment: "")
55 | static let off = NSLocalizedString("off", comment: "")
56 | }
57 |
--------------------------------------------------------------------------------
/Tests/CwlViewsCatalog_macOS/Sources/SliderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SliderView.swift
3 | // CwlViewsCatalog_iOS
4 | //
5 | // Created by Matt Gallagher on 10/2/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import CwlViews
21 |
22 | struct SliderViewState: CodableContainer {
23 | let value: Var
24 | init() {
25 | value = Var(.initial)
26 | }
27 | }
28 |
29 | func sliderView(_ sliderViewState: SliderViewState) -> ViewConvertible {
30 | return View(
31 | .layout -- .center(
32 | .view(
33 | TextField.label(
34 | .stringValue <-- sliderViewState.value.allChanges().map { .localizedStringWithFormat(.valueFormat, $0, Double.max) },
35 | .font -- NSFont.monospacedDigitSystemFont(ofSize: 20, weight: .regular)
36 | )
37 | ),
38 | .space(),
39 | .view(
40 | Slider(
41 | .isContinuous -- true,
42 | .minValue -- .min,
43 | .maxValue -- .max,
44 | .doubleValue <-- sliderViewState.value,
45 | .action(\.doubleValue) --> sliderViewState.value.update()
46 | )
47 | )
48 | )
49 | )
50 | }
51 |
52 | private extension Double {
53 | static let min: Double = 0
54 | static let max: Double = 500
55 | static let initial: Double = 0
56 | }
57 |
58 | private extension String {
59 | static let valueFormat = NSLocalizedString("%.1f of %.1f", comment: "")
60 | }
61 |
--------------------------------------------------------------------------------
/Sources/CwlViews/Core/Mutation/CwlStackMutation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlStackMutation.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 2017/10/22.
6 | // Copyright © 2017 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | public enum StackMutation: ExpressibleByArrayLiteral {
22 | public init(arrayLiteral elements: Value...) {
23 | self = .reload(elements)
24 | }
25 |
26 | public typealias ArrayLiteralElement = Value
27 |
28 | case push(Value)
29 | case pop
30 | case popToCount(Int)
31 | case reload([Value])
32 |
33 | func apply(to stack: inout Array) {
34 | switch self {
35 | case .push(let v): stack.append(v)
36 | case .pop: stack.removeLast()
37 | case .popToCount(let c): stack.removeLast(stack.count - c)
38 | case .reload(let newStack): stack = newStack
39 | }
40 | }
41 | }
42 |
43 | extension SignalInterface {
44 | public func stackMap(_ transform: @escaping (A) -> B) -> Signal> where OutputValue == StackMutation {
45 | return map { m in
46 | switch m {
47 | case .push(let a): return StackMutation.push(transform(a))
48 | case .pop: return StackMutation.pop
49 | case .popToCount(let i): return StackMutation.popToCount(i)
50 | case .reload(let array): return StackMutation.reload(array.map { transform($0) })
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/CwlSignal.xcodeproj/xcshareddata/xcschemes/CwlSignal_tvOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/CwlUtils.xcodeproj/xcshareddata/xcschemes/CwlUtils_tvOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/Tests/CwlUtilsTests/CwlSysctlTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlSysctlTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/02/03.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | class SysctlTests: XCTestCase {
26 | func testSysctl() {
27 | let hostName = Sysctl.hostName
28 | XCTAssert(hostName != "")
29 |
30 | let machine = Sysctl.machine
31 |
32 | #if arch(x86_64)
33 | XCTAssert(machine == "x86_64")
34 | #else
35 | XCTAssert(machine != "")
36 | #endif
37 |
38 | let model = Sysctl.model
39 | XCTAssert(model != "")
40 |
41 | let osRelease = Sysctl.osRelease
42 | XCTAssert(osRelease != "")
43 |
44 | let osType = Sysctl.osType
45 | XCTAssert(osType == "Darwin")
46 |
47 | let osVersion = Sysctl.osVersion
48 | XCTAssert(osVersion != "")
49 |
50 | let version = Sysctl.version
51 | XCTAssert(version.hasPrefix("Darwin Kernel Version"))
52 |
53 | let activeCPUs = Sysctl.activeCPUs
54 | XCTAssert(activeCPUs > 0)
55 |
56 | #if os(macOS)
57 | let osRev = Sysctl.osRev
58 | XCTAssert(osRev != 0)
59 |
60 | let cpuFreq = Sysctl.cpuFreq
61 | XCTAssert(cpuFreq > 1_000_000_000)
62 |
63 | let memSize = Sysctl.memSize
64 | XCTAssert(memSize > 1_000_000_000)
65 | #endif
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/CwlSignal.xcodeproj/xcshareddata/xcschemes/CwlSignal_macOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/CwlSignal.xcodeproj/xcshareddata/xcschemes/CwlSignal_iOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Sources/CwlViews/Core/Adapter/CwlMasterDetail.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlMasterDetail.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 3/1/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | /// An "Either" type for use in scenarios where "Equatable" and "Codable" are required but there's only ever a single "Master" instance so equality is implied. This is common in Navigation Controller stacks and Split Views.
21 | public enum MasterDetail: CodableContainer {
22 | case master(Master)
23 | case detail(Detail)
24 |
25 | public var childCodableContainers: [CodableContainer] {
26 | switch self {
27 | case .master(let tvm): return [tvm]
28 | case .detail(let dvm): return [dvm]
29 | }
30 | }
31 |
32 | enum Keys: CodingKey { case master, detail }
33 |
34 | public func encode(to encoder: Encoder) throws {
35 | var c = encoder.container(keyedBy: Keys.self)
36 | switch self {
37 | case .master(let tvm): try c.encode(tvm, forKey: .master)
38 | case .detail(let dvm): try c.encode(dvm, forKey: .detail)
39 | }
40 | }
41 |
42 | public init(from decoder: Decoder) throws {
43 | let c = try decoder.container(keyedBy: Keys.self)
44 | if let tvm = try c.decodeIfPresent(Master.self, forKey: .master) {
45 | self = .master(tvm)
46 | } else {
47 | self = .detail(try c.decode(Detail.self, forKey: .detail))
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/CwlUtils.xcodeproj/xcshareddata/xcschemes/CwlUtils_macOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Sources/CwlViewsTesting/macOS/CwlPanGestureRecognizerTesting_macOS.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlPanGestureRecognizer_macOS.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 11/3/16.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | #if os(macOS)
21 |
22 | extension BindingParser where Downcast: PanGestureRecognizerBinding {
23 | // You can easily convert the `Binding` cases to `BindingParser` using the following Xcode-style regex:
24 | // Replace: case ([^\(]+)\((.+)\)$
25 | // With: public static var $1: BindingParser<$2, PanGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .$1(let x) = \$0 { return x } else { return nil } }, upcast: { \$0.asPanGestureRecognizerBinding() }) }
26 |
27 | // 0. Static bindings are applied at construction and are subsequently immutable.
28 |
29 | // 1. Value bindings may be applied at construction and may subsequently change.
30 | public static var buttonMask: BindingParser, PanGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .buttonMask(let x) = $0 { return x } else { return nil } }, upcast: { $0.asPanGestureRecognizerBinding() }) }
31 |
32 | // 2. Signal bindings are performed on the object after construction.
33 |
34 | // 3. Action bindings are triggered by the object after construction.
35 |
36 | // 4. Delegate bindings require synchronous evaluation within the object's context.
37 | }
38 |
39 | #endif
40 |
--------------------------------------------------------------------------------
/Sources/CwlViewsTesting/iOS/CwlPinchGestureRecognizerTesting_iOS.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlPinchGestureRecognizer_macOS.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 11/3/16.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | #if os(iOS)
21 |
22 | extension BindingParser where Downcast: PinchGestureRecognizerBinding {
23 | // You can easily convert the `Binding` cases to `BindingParser` using the following Xcode-style regex:
24 | // Replace: case ([^\(]+)\((.+)\)$
25 | // With: public static var $1: BindingParser<$2, PinchGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .$1(let x) = \$0 { return x } else { return nil } }, upcast: { \$0.asPinchGestureRecognizerBinding() }) }
26 |
27 | // 0. Static bindings are applied at construction and are subsequently immutable.
28 |
29 | // 1. Value bindings may be applied at construction and may subsequently change.
30 | public static var scale: BindingParser, PinchGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .scale(let x) = $0 { return x } else { return nil } }, upcast: { $0.asPinchGestureRecognizerBinding() }) }
31 |
32 | // 2. Signal bindings are performed on the object after construction.
33 |
34 | // 3. Action bindings are triggered by the object after construction.
35 |
36 | // 4. Delegate bindings require synchronous evaluation within the object's context.
37 | }
38 |
39 | #endif
40 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/CwlUtils.xcodeproj/xcshareddata/xcschemes/CwlUtils_iOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/Tests/CwlUtilsPerformanceTests/CwlDequePerformanceTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlDequePerformanceTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2016/09/13.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | #if DEBUG
26 | let outerCount = 100
27 | #else
28 | let outerCount = 100_000
29 | #endif
30 |
31 | let innerCount = 20
32 |
33 | class DequePerformanceTests: XCTestCase {
34 | func testFIFOPerformance() {
35 | measure { () -> Void in
36 | var accumulator = 0
37 | for _ in 1...outerCount {
38 | var deque = Deque()
39 | for i in 1...innerCount {
40 | deque.append(i)
41 | accumulator ^= (deque.last ?? 0)
42 | }
43 | for _ in 1...innerCount {
44 | accumulator ^= (deque.first ?? 0)
45 | deque.remove(at: 0)
46 | }
47 | }
48 | XCTAssert(accumulator == 0)
49 | }
50 | }
51 |
52 | func testReferenceArrayPerformance() {
53 | measure { () -> Void in
54 | var accumulator = 0
55 | for _ in 1...outerCount {
56 | var deque = Array()
57 | for i in 1...innerCount {
58 | deque.append(i)
59 | accumulator ^= (deque.last ?? 0)
60 | }
61 | for _ in 1...innerCount {
62 | accumulator ^= (deque.first ?? 0)
63 | deque.remove(at: 0)
64 | }
65 | }
66 | XCTAssert(accumulator == 0)
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Sources/CwlViewsTesting/iOS/CwlRotationGestureRecognizerTesting_iOS.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlRotationGestureRecognizer_macOS.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 11/3/16.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | #if os(iOS)
21 |
22 | extension BindingParser where Downcast: RotationGestureRecognizerBinding {
23 | // You can easily convert the `Binding` cases to `BindingParser` using the following Xcode-style regex:
24 | // Replace: case ([^\(]+)\((.+)\)$
25 | // With: public static var $1: BindingParser<$2, RotationGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .$1(let x) = \$0 { return x } else { return nil } }, upcast: { \$0.asRotationGestureRecognizerBinding() }) }
26 |
27 | // 0. Static bindings are applied at construction and are subsequently immutable.
28 |
29 | // 1. Value bindings may be applied at construction and may subsequently change.
30 | public static var rotation: BindingParser, RotationGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .rotation(let x) = $0 { return x } else { return nil } }, upcast: { $0.asRotationGestureRecognizerBinding() }) }
31 |
32 | // 2. Signal bindings are performed on the object after construction.
33 |
34 | // 3. Action bindings are triggered by the object after construction.
35 |
36 | // 4. Delegate bindings require synchronous evaluation within the object's context.
37 | }
38 |
39 | #endif
40 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/CwlSignal.playground/Pages/Advanced behaviors - Capturing.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 | # Advanced behaviors 3
3 |
4 | > **This playground requires the CwlSignal.framework built by the CwlSignal_macOS scheme.** If you're seeing errors finding or building module 'CwlSignal', follow the Build Instructions on the [Contents](Contents) page.
5 |
6 | ## Capturing
7 |
8 | SignalCapture allows activation values to be pulled synchronously from a signal. This provides potential solutions to scenarios where code cannot proceed without being able to obtain an immediate value. Simply put: activation in CwlSignal provides pull-based synchronous behaviors, whereas typical reactive programming is push-based and potentially asynchronous.
9 |
10 | The `peek()` function on `Signal` and the helper type `SignalLatest` provide slightly different semantics but can also be used to synchronously obtain values from outside of the stream when interface constraints demand it.
11 |
12 | ---
13 | */
14 | import CwlSignal
15 |
16 | // Create an input/output pair, transforming the output before returning
17 | // SOMETHING TO TRY: replace `.continuous()` with `.playback()`
18 | let pair = Signal.channel().continuous()
19 |
20 | // The `continuous` signal will cache the most recently sent value
21 | pair.input.send(1)
22 | pair.input.send(2)
23 |
24 | // Capture the "2" activation value cached by the `continuous` signal
25 | let capture = pair.signal.capture()
26 |
27 | print("Activation: \(capture.values)")
28 |
29 | // Capturing blocks signal delivery so *both* of these will be queued for later
30 | pair.input.send(3)
31 | pair.input.send(4)
32 |
33 | print("Values sent during capture are paused until we subscribe.")
34 |
35 | // Subscribing unblocks the signal so the "3" and the "4" will now be sent through.
36 | // SOMETHING TO TRY: replace `subscribeValues` with `subscribeValues(resend: true)`
37 | let out = capture.subscribeValues { value in print("Value: \(value)") }
38 |
39 | /*:
40 | ---
41 |
42 | *This example writes to the "Debug Area". If it is not visible, show it from the menubar: "View" → "Debug Area" → "Show Debug Area".*
43 |
44 | [Next page: Advanced composition - nested operators](@next)
45 |
46 | [Previous page: Advanced behaviors - lazy generation](@previous)
47 | */
48 |
--------------------------------------------------------------------------------
/Sources/CwlViewsTesting/macOS/CwlRotationGestureRecognizerTesting_macOS.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlRotationGestureRecognizer_macOS.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 11/3/16.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | #if os(macOS)
21 |
22 | extension BindingParser where Downcast: RotationGestureRecognizerBinding {
23 | // You can easily convert the `Binding` cases to `BindingParser` using the following Xcode-style regex:
24 | // Replace: case ([^\(]+)\((.+)\)$
25 | // With: public static var $1: BindingParser<$2, RotationGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .$1(let x) = \$0 { return x } else { return nil } }, upcast: { \$0.asRotationGestureRecognizerBinding() }) }
26 |
27 | // 0. Static bindings are applied at construction and are subsequently immutable.
28 |
29 | // 1. Value bindings may be applied at construction and may subsequently change.
30 | public static var rotationInRadians: BindingParser, RotationGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .rotationInRadians(let x) = $0 { return x } else { return nil } }, upcast: { $0.asRotationGestureRecognizerBinding() }) }
31 |
32 | // 2. Signal bindings are performed on the object after construction.
33 |
34 | // 3. Action bindings are triggered by the object after construction.
35 |
36 | // 4. Delegate bindings require synchronous evaluation within the object's context.
37 | }
38 |
39 | #endif
40 |
--------------------------------------------------------------------------------
/Sources/CwlViewsTesting/iOS/CwlScreenEdgePanGestureRecognizerTesting_iOS.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlScreenEdgePanGestureRecognizer_macOS.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 11/3/16.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | #if os(iOS)
21 |
22 | extension BindingParser where Downcast: ScreenEdgePanGestureRecognizerBinding {
23 | // You can easily convert the `Binding` cases to `BindingParser` using the following Xcode-style regex:
24 | // Replace: case ([^\(]+)\((.+)\)$
25 | // With: public static var $1: BindingParser<$2, ScreenEdgePanGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .$1(let x) = \$0 { return x } else { return nil } }, upcast: { \$0.asScreenEdgePanGestureRecognizerBinding() }) }
26 |
27 | // 0. Static bindings are applied at construction and are subsequently immutable.
28 |
29 | // 1. Value bindings may be applied at construction and may subsequently change.
30 | public static var edges: BindingParser, ScreenEdgePanGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .edges(let x) = $0 { return x } else { return nil } }, upcast: { $0.asScreenEdgePanGestureRecognizerBinding() }) }
31 |
32 | // 2. Signal bindings are performed on the object after construction.
33 |
34 | // 3. Action bindings are triggered by the object after construction.
35 |
36 | // 4. Delegate bindings require synchronous evaluation within the object's context.
37 | }
38 |
39 | #endif
40 |
--------------------------------------------------------------------------------
/Sources/CwlViews/Core/Binder/CwlBinderApplyable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlBinderApplyable.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 31/12/18.
6 | // Copyright © 2018 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | /// Preparers usually default construct the `Storage` except in specific cases where the storage needs a reference to the instance.
21 | public protocol BinderApplyable: BinderPreparer {
22 | /// Constructs the `Storage`
23 | ///
24 | /// - Returns: the storage
25 | func constructStorage(instance: Instance) -> Storage
26 |
27 | /// - Returns: the output, after tying the lifetimes of the instance and storage together
28 | func combine(lifetimes: [Lifetime], instance: Instance, storage: Storage) -> Output
29 | }
30 |
31 | public extension BinderApplyable {
32 | static func bind(_ bindings: [Binding], to source: (_ preparer: Self) -> Instance) -> (Self, Instance, Storage, [Lifetime]) {
33 | var preparer = Self()
34 | for b in bindings {
35 | preparer.prepareBinding(b)
36 | }
37 |
38 | var lifetimes = [Lifetime]()
39 | let instance = source(preparer)
40 | let storage = preparer.constructStorage(instance: instance)
41 |
42 | preparer.prepareInstance(instance, storage: storage)
43 |
44 | for b in bindings {
45 | lifetimes += preparer.applyBinding(b, instance: instance, storage: storage)
46 | }
47 |
48 | lifetimes += preparer.finalizeInstance(instance, storage: storage)
49 |
50 | return (preparer, instance, storage, lifetimes)
51 | }
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/Dependencies/CwlPreconditionTesting/CwlPreconditionTesting.xcodeproj/xcshareddata/xcschemes/CwlPreconditionTesting_tvOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Sources/CwlViewsTesting/macOS/CwlMagnificationGestureRecognizerTesting_macOS.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlMagnificationGestureRecognizer_macOS.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 11/3/16.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | #if os(macOS)
21 |
22 | extension BindingParser where Downcast: MagnificationGestureRecognizerBinding {
23 | // You can easily convert the `Binding` cases to `BindingParser` using the following Xcode-style regex:
24 | // Replace: case ([^\(]+)\((.+)\)$
25 | // With: public static var $1: BindingParser<$2, MagnificationGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .$1(let x) = \$0 { return x } else { return nil } }, upcast: { \$0.asMagnificationGestureRecognizerBinding() }) }
26 |
27 | // 0. Static bindings are applied at construction and are subsequently immutable.
28 |
29 | // 1. Value bindings may be applied at construction and may subsequently change.
30 | public static var magnification: BindingParser, MagnificationGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .magnification(let x) = $0 { return x } else { return nil } }, upcast: { $0.asMagnificationGestureRecognizerBinding() }) }
31 |
32 | // 2. Signal bindings are performed on the object after construction.
33 |
34 | // 3. Action bindings are triggered by the object after construction.
35 |
36 | // 4. Delegate bindings require synchronous evaluation within the object's context.
37 | }
38 |
39 | #endif
40 |
--------------------------------------------------------------------------------
/Tests/CwlViewsCatalog_iOS/Sources/SliderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SliderView.swift
3 | // CwlViewsCatalog_iOS
4 | //
5 | // Created by Matt Gallagher on 10/2/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import CwlViews
21 |
22 | struct SliderViewState: CodableContainer {
23 | let value: Var
24 | init() {
25 | value = Var(.initial)
26 | }
27 | }
28 |
29 | func sliderView(_ sliderViewState: SliderViewState, _ navigationItem: NavigationItem) -> ViewControllerConvertible {
30 | return ViewController(
31 | .navigationItem -- navigationItem,
32 | .view -- View(
33 | .backgroundColor -- .white,
34 | .layout -- .center(marginEdges: .allLayout,
35 | .view(
36 | Label(
37 | .text <-- sliderViewState.value.allChanges().map { .localizedStringWithFormat(.valueFormat, $0, Float.max) },
38 | .font -- UIFont.monospacedDigitSystemFont(ofSize: 20, weight: .regular)
39 | )
40 | ),
41 | .space(),
42 | .view(
43 | Slider(
44 | .isContinuous -- true,
45 | .minimumValue -- .min,
46 | .maximumValue -- .max,
47 | .value <-- sliderViewState.value.animate(),
48 | .action(.valueChanged, \.value) --> sliderViewState.value.update()
49 | )
50 | )
51 | )
52 | )
53 | )
54 | }
55 |
56 | private extension Float {
57 | static let min: Float = 0
58 | static let max: Float = 500
59 | static let initial: Float = 100
60 | }
61 |
62 | private extension String {
63 | static let valueFormat = NSLocalizedString("%.1f of %.1f", comment: "")
64 | }
65 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/Dependencies/CwlPreconditionTesting/CwlPreconditionTesting.xcodeproj/xcshareddata/xcschemes/CwlPreconditionTesting_iOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/Dependencies/CwlPreconditionTesting/CwlPreconditionTesting.xcodeproj/xcshareddata/xcschemes/CwlPreconditionTesting_macOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/Dependencies/CwlPreconditionTesting/Dependencies/CwlCatchException/CwlCatchException.xcodeproj/xcshareddata/xcschemes/CwlCatchException_iOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/Dependencies/CwlPreconditionTesting/Dependencies/CwlCatchException/CwlCatchException.xcodeproj/xcshareddata/xcschemes/CwlCatchException_macOSTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
39 |
40 |
41 |
42 |
48 |
49 |
51 |
52 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/CwlSignal.playground/Pages/Parallel composition - combine.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 |
3 | # Parallel composition 1
4 |
5 | > **This playground requires the CwlSignal.framework built by the CwlSignal_macOS scheme.** If you're seeing errors finding or building module 'CwlSignal', follow the Build Instructions on the [Contents](Contents) page.
6 |
7 | ## The `combine` function
8 |
9 | Another strength of reactive programming is the ability to integrate dependencies from different sources in a thread-safe manner.
10 |
11 | CwlSignal offers the `combine` operator which has an interface close to that of the `transform` operator, except that incoming `Result`s are wrapped in an `EitherResult`, reflecting an origin from "either" the first, the second or possibly third, fourth or fifth different input `Signal`.
12 |
13 | ---
14 | */
15 | import CwlSignal
16 |
17 | let semaphore = DispatchSemaphore(value: 0)
18 |
19 | // Two signals compete, over time
20 | let intSignal = Signal.timer(interval: .seconds(1), value: 1234)
21 | let doubleSignal = Signal.timer(interval: .interval(0.5), value: 0.1234)
22 |
23 | // The signals are combined – first to send a value wins
24 | // SOMETHING TO TRY: change the `fromSeconds` timing values, above, to let the `Int` signal arrive first.
25 | let output = intSignal.combine(doubleSignal) { either -> Signal.Next in
26 | switch either {
27 | case .result1(.success(let intValue)): return .value("integer \(intValue)", end: .complete)
28 | case .result2(.success(let doubleValue)): return .value("double \(doubleValue)", end: .complete)
29 | default: return .complete()
30 | }
31 | // NOTE: always completes after the first value
32 | }.subscribe { result in
33 | switch result {
34 | case .success(let v): print("The first value received is: \(v)")
35 | case .failure: print("Signal complete"); semaphore.signal()
36 | }
37 | }
38 |
39 | // In reactive programming, blocking is normally "bad" but we need to block or the playground will finish before the background work.
40 | semaphore.wait()
41 |
42 | /*:
43 | ---
44 |
45 | *This example writes to the "Debug Area". If it is not visible, show it from the menubar: "View" → "Debug Area" → "Show Debug Area".*
46 |
47 | [Next page: Advanced behaviors - continuous](@next)
48 |
49 | [Previous page: Serial pipelines - asynchronous](@previous)
50 | */
51 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/Tests/CwlUtils_macOSTestApp/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // CwlUtils_macOSTestApp
4 | //
5 | // Created by Matt Gallagher on 2017/01/31.
6 | // Copyright © 2017 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Cocoa
22 | import CwlUtils
23 |
24 | func showAlert(error: Error) {
25 | NSAlert(error: error).runModal()
26 | }
27 |
28 | class ViewController: NSViewController {
29 |
30 | let b = NSButton()
31 |
32 | override func viewDidLoad() {
33 | super.viewDidLoad()
34 |
35 | // Do any additional setup after loading the view.
36 | }
37 |
38 | override var representedObject: Any? {
39 | didSet {
40 | // Update the view, if already loaded.
41 | }
42 | }
43 |
44 | func process(data: NSData) {
45 | }
46 |
47 | func someProcessingTask1(path: String) {
48 | do {
49 | let data = try NSData(contentsOfFile: path, options: .mappedIfSafe)
50 | process(data: data)
51 | } catch {
52 | showAlert(error: error)
53 | }
54 | }
55 |
56 | @IBAction func someUserAction1(_ sender: AnyObject) {
57 | someProcessingTask1(path: "/invalid/path")
58 | }
59 |
60 | func someProcessingTask2(path: String) throws {
61 | try rethrowUnanticipated {
62 | let data = try NSData(contentsOfFile: path, options: .mappedIfSafe)
63 | process(data: data)
64 | }
65 | }
66 |
67 | @IBAction func someUserAction2(_ sender: AnyObject) {
68 | do {
69 | try someProcessingTask2(path: "/invalid/path")
70 | } catch {
71 | presentError(error)
72 | }
73 | }
74 | }
75 |
76 |
--------------------------------------------------------------------------------
/Sources/CwlViews/Core/BindingValues/CwlScopedValues.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScopedValues.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 2017/03/23.
6 | // Copyright © 2017 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import Foundation
21 |
22 | public struct ScopedValues: ExpressibleByArrayLiteral {
23 | public typealias ArrayLiteralElement = ScopedValues
24 |
25 | public let pairs: [(scope: Scope, value: Value)]
26 |
27 | public init(arrayLiteral elements: ScopedValues...) {
28 | self.pairs = elements.flatMap { $0.pairs }
29 | }
30 |
31 | public init(pairs: [(Scope, Value)]) {
32 | self.pairs = pairs
33 | }
34 |
35 | public init(scope: Scope, value: Value) {
36 | self.pairs = [(scope, value)]
37 | }
38 |
39 | public static func value(_ value: Value, for scope: Scope) -> ScopedValues {
40 | return ScopedValues(scope: scope, value: value)
41 | }
42 | }
43 |
44 | extension Dynamic {
45 | // Gets the subsequent (i.e. after construction) values from the `Dynamic`
46 | public func apply(instance: I, removeOld: @escaping (I, Scope, V) -> Void, applyNew: @escaping (I, Scope, V) -> Void) -> Lifetime? where ScopedValues == Value {
47 | var previous: ScopedValues? = nil
48 | return apply(instance) { i, v in
49 | for (scope, value) in previous?.pairs ?? [] {
50 | removeOld(instance, scope, value)
51 | }
52 | previous = v
53 | for (scope, value) in v.pairs {
54 | applyNew(instance, scope, value)
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Tests/CwlViewsCatalog_macOS/Sources/TextFieldView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TextFieldView.swift
3 | // CwlViewsCatalog_iOS
4 | //
5 | // Created by Matt Gallagher on 10/2/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import CwlViews
21 | import NaturalLanguage
22 |
23 | struct TextFieldViewState: CodableContainer {
24 | let text: Var
25 | init() {
26 | text = Var("")
27 | }
28 | }
29 |
30 | func textFieldView(_ textFieldViewState: TextFieldViewState) -> ViewConvertible {
31 | return View(
32 | .layout -- .center(
33 | .view(
34 | TextField.wrappingLabel(
35 | .font -- .preferredFont(forTextStyle: .label, size: .controlSmall, weight: .semibold),
36 | .stringValue <-- textFieldViewState.text.allChanges().map { text in
37 | .localizedStringWithFormat(.labelFormat, text.count, text.wordCount)
38 | }
39 | )
40 | ),
41 | .space(),
42 | .view(
43 | length: .fillRemaining,
44 | TextField(
45 | .stringValue <-- textFieldViewState.text,
46 | .stringChanged() --> textFieldViewState.text.update(),
47 | .bezelStyle -- .roundedBezel,
48 | .becomeFirstResponder <-- Signal.timer(interval: .seconds(0), value: (), context: .main)
49 | )
50 | )
51 | )
52 | )
53 | }
54 |
55 | private extension String {
56 | static let labelFormat = NSLocalizedString("Field contains %ld characters and %ld words.", comment: "")
57 |
58 | var wordCount: Int {
59 | let tokenizer = NLTokenizer(unit: .word)
60 | tokenizer.string = self
61 | return tokenizer.tokens(for: startIndex.. Instance
27 | }
28 |
29 | public extension BinderConstructor where Instance: DefaultConstructable {
30 | func constructInstance(type: Instance.Type, parameters: Parameters) -> Instance {
31 | return type.init()
32 | }
33 | }
34 |
35 | /// All NSObject instances can use AssociatedBinderStorage which embeds lifetimes in the Objective-C associated object storage.
36 | public protocol BinderEmbedder: BinderApplyable where Instance: NSObjectProtocol, Storage: AssociatedBinderStorage, Output == Instance {}
37 | public extension BinderEmbedder {
38 | func combine(lifetimes: [Lifetime], instance: Instance, storage: Storage) -> Output {
39 | storage.embed(lifetimes: lifetimes, in: instance)
40 | return instance
41 | }
42 | }
43 |
44 | /// A `BinderEmbedderConstructor` is the standard configuration for a constructable NSObject.
45 | public typealias BinderEmbedderConstructor = BinderEmbedder & BinderConstructor
46 |
--------------------------------------------------------------------------------
/Sources/CwlViews/Core/Adapter/CwlAdapter+Signals.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlAdapter+Signals.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 15/1/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | extension Adapter: Lifetime {
21 | public func cancel() {
22 | if State.self is CodableContainer.Type, let value = combinedSignal.peek()?.state, var sc = value as? CodableContainer {
23 | sc.cancel()
24 | }
25 | input.cancel()
26 | }
27 | }
28 |
29 | extension Adapter: Codable where State: Codable {
30 | public init(from decoder: Decoder) throws {
31 | let c = try decoder.singleValueContainer()
32 | let p = try c.decode(State.self)
33 | self.init(adapterState: p)
34 | }
35 |
36 | public func encode(to encoder: Encoder) throws {
37 | if let s = combinedSignal.peek()?.state {
38 | var c = encoder.singleValueContainer()
39 | try c.encode(s)
40 | }
41 | }
42 | }
43 |
44 | extension Adapter: CodableContainer where State: PersistentAdapterState {
45 | public var childCodableContainers: [CodableContainer] {
46 | if let state = combinedSignal.peek()?.state {
47 | return (state as? CodableContainer)?.childCodableContainers ?? []
48 | } else {
49 | return []
50 | }
51 | }
52 |
53 | public var codableValueChanged: Signal {
54 | if State.self is CodableContainer.Type {
55 | return combinedSignal.flatMapLatest { (content: State.Output) -> Signal in
56 | let cc = content.state as! CodableContainer
57 | return cc.codableValueChanged.startWith(())
58 | }.dropActivation()
59 | }
60 | return combinedSignal.map { _ in () }.dropActivation()
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/Tests/CwlUtilsTests/CwlMutexTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlMutexTests.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 | import XCTest
23 | import CwlUtils
24 |
25 | class PthreadTests: XCTestCase {
26 | func testPthreadMutex() {
27 | let mutex1 = PThreadMutex()
28 |
29 | let e1 = expectation(description: "Block1 not invoked")
30 | mutex1.sync {
31 | e1.fulfill()
32 | let reenter: Void? = mutex1.trySync() {
33 | XCTFail()
34 | }
35 | XCTAssert(reenter == nil)
36 | }
37 |
38 | let mutex2 = PThreadMutex(type: .recursive)
39 |
40 | let e2 = expectation(description: "Block2 not invoked")
41 | let e3 = expectation(description: "Block3 not invoked")
42 | mutex2.sync {
43 | e2.fulfill()
44 | let reenter: Void? = mutex2.trySync() {
45 | e3.fulfill()
46 | }
47 | XCTAssert(reenter != nil)
48 | }
49 |
50 | let e4 = expectation(description: "Block4 not invoked")
51 | let r = mutex1.sync { () -> Int in
52 | e4.fulfill()
53 | let reenter: Void? = mutex1.trySync() {
54 | XCTFail()
55 | }
56 | XCTAssert(reenter == nil)
57 | return 13
58 | }
59 | XCTAssert(r == 13)
60 |
61 | waitForExpectations(timeout: 0, handler: nil)
62 | }
63 | }
64 |
65 | extension PThreadMutex {
66 | public func sync_generic_param(_ param: inout T, f: (inout T) throws -> R) rethrows -> R {
67 | pthread_mutex_lock(&underlyingMutex)
68 | defer { pthread_mutex_unlock(&underlyingMutex) }
69 | return try f(¶m)
70 | }
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/Tests/CwlViewsCatalog_iOS/Sources/StepperView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StepperView.swift
3 | // CwlViewsCatalog_iOS
4 | //
5 | // Created by Sye Boddeus on 13/5/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import CwlViews
21 |
22 | struct StepperViewState: CodableContainer {
23 | let value: Var
24 | init() {
25 | value = Var(.initial)
26 | }
27 | }
28 |
29 | func stepperlView(_ viewState: StepperViewState, _ navigationItem: NavigationItem) -> ViewControllerConvertible {
30 | return ViewController(
31 | .navigationItem -- navigationItem,
32 | .view -- View(
33 | .backgroundColor -- .white,
34 | .layout -- .center(
35 | marginEdges: .allLayout,
36 | length: .equalTo(constant: 100.0),
37 | breadth: .equalTo(constant: 400.0),
38 | .vertical(
39 | align: .center,
40 | .view(Label(.text <-- viewState.value.allChanges().map { value in "\(value)" })),
41 | .space(),
42 | .view(
43 | Stepper(
44 | .minimumValue -- .min,
45 | .maximumValue -- .max,
46 | .isContinuous -- true,
47 | .stepValue -- .step,
48 | .tintColor -- .purple,
49 | .decrementImage -- .normal(.drawn(width: 10, height: 10) { $0.fillEllipse(in: $1) }),
50 | .incrementImage -- .normal(.drawn(width: 10, height: 10) { $0.fill($1) }),
51 | .value <-- viewState.value,
52 | .action(.valueChanged, \.value) --> viewState.value.update()
53 | )
54 | )
55 | )
56 | )
57 | )
58 | )
59 | }
60 |
61 | private extension Double {
62 | static let initial = 10.5
63 | static let max = 100.0
64 | static let min = 0.0
65 | static let step = 1.5
66 | }
67 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/CwlSignal.playground/Pages/Serial pipelines - asynchronous.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | /*:
2 |
3 | # Serial pipelines 4: asynchrony
4 |
5 | > **This playground requires the CwlSignal.framework built by the CwlSignal_macOS scheme.** If you're seeing errors finding or building module 'CwlSignal', follow the Build Instructions on the [Contents](Contents) page.
6 |
7 | ## Using the `context` parameter.
8 |
9 | Most functions in CwlSignal that accept a closure or other processing function will also take a `context` parameter immediately before the closure. This `context` specifies the execution context where the closure or function should run.
10 |
11 | By default, this `context` is `.direct` (specifying that the closure should be directly invoked like a regular function) but you can specify an asynchronous context (like one of the Dispatch global concurrent queues or a private queue) to have the signal processed asychronously. In this way, reactive programming can snake across multiple contexts, multiple threads and multiple delays over time.
12 |
13 | > `Signal` will ensure that values sent through the channel are delivered in-order, even if the context is concurrent.
14 |
15 | ---
16 | */
17 | import CwlSignal
18 |
19 | let semaphore = DispatchSemaphore(value: 0)
20 | let completionContext = Exec.asyncQueue()
21 |
22 | // Create an input/output pair
23 | let (input, output) = Signal.channel()
24 | .map(context: .global) { value in
25 | // Perform the background work on the default global concurrent DispatchQueue
26 | return sqrt(Double(value))
27 | }
28 | .subscribe(context: completionContext) { result in
29 | // Deliver to a completion thread.
30 | switch result {
31 | case .success(let value): print(value)
32 | case .failure: print("Done"); semaphore.signal()
33 | }
34 | }
35 |
36 | // Send values to the input end
37 | input.send(1, 2, 3, 4, 5, 6, 7, 8, 9)
38 | input.complete()
39 |
40 | // In reactive programming, blocking is normally discouraged (you should subscribe to all
41 | // dependencies and process when they're all done) but we need to block or the playground
42 | // will finish before the background work.
43 | semaphore.wait()
44 |
45 | /*:
46 | ---
47 |
48 | *This example writes to the "Debug Area". If it is not visible, show it from the menubar: "View" → "Debug Area" → "Show Debug Area".*
49 |
50 | [Next page: Parallel composition - combine](@next)
51 |
52 | [Previous page: Serial pipelines - channel](@previous)
53 | */
54 |
--------------------------------------------------------------------------------
/Sources/CwlViewsTesting/macOS/CwlTableCellViewTesting_macOS.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlTableCellView_macOS.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 13/11/2015.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | #if os(macOS)
21 |
22 | extension BindingParser where Downcast: TableCellViewBinding {
23 | // You can easily convert the `Binding` cases to `BindingParser` using the following Xcode-style regex:
24 | // Replace: case ([^\(]+)\((.+)\)$
25 | // With: public static var $1: BindingParser<$2, TableCellView.Binding, Downcast> { return .init(extract: { if case .$1(let x) = \$0 { return x } else { return nil } }, upcast: { \$0.asTableCellViewBinding() }) }
26 |
27 | // 0. Static bindings are applied at construction and are subsequently immutable.
28 |
29 | // 1. Value bindings may be applied at construction and may subsequently change.
30 | public static var backgroundStyle: BindingParser, TableCellView.Binding, Downcast> { return .init(extract: { if case .backgroundStyle(let x) = $0 { return x } else { return nil } }, upcast: { $0.asTableCellViewBinding() }) }
31 | public static var rowSizeStyle: BindingParser, TableCellView.Binding, Downcast> { return .init(extract: { if case .rowSizeStyle(let x) = $0 { return x } else { return nil } }, upcast: { $0.asTableCellViewBinding() }) }
32 |
33 | // 2. Signal bindings are performed on the object after construction.
34 |
35 | // 3. Action bindings are triggered by the object after construction.
36 |
37 | // 4. Delegate bindings require synchronous evaluation within the object's context.
38 | }
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/Dependencies/CwlSignal/Dependencies/CwlUtils/Sources/CwlUtils/CwlResult.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlResult.swift
3 | // CwlUtils
4 | //
5 | // Created by Matt Gallagher on 2015/02/03.
6 | // Copyright © 2015 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any
9 | // purpose with or without fee is hereby granted, provided that the above
10 | // copyright notice and this permission notice appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
15 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
18 | // IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 | //
20 |
21 | import Foundation
22 |
23 | /// Either a Success value or an Failure error
24 | public extension Result {
25 | /// Convenience tester/getter for the value
26 | var value: Success? {
27 | switch self {
28 | case .success(let s): return s
29 | case .failure: return nil
30 | }
31 | }
32 |
33 | /// Convenience tester/getter for the error
34 | var error: Failure? {
35 | switch self {
36 | case .success: return nil
37 | case .failure(let f): return f
38 | }
39 | }
40 |
41 | /// Test whether the result is an error.
42 | var isSuccess: Bool {
43 | return !isFailure
44 | }
45 |
46 | /// Test whether the result is an error.
47 | var isFailure: Bool {
48 | switch self {
49 | case .success: return false
50 | case .failure: return true
51 | }
52 | }
53 | }
54 |
55 | public extension Result where Failure == Swift.Error {
56 | /// Chains another Result to this one. In the event that this Result is a .Success, the provided transformer closure is used to transform the value into another value (of a potentially new type) and a new Result is made from that value. In the event that this Result is a .Failure, the next Result will have the same error as this one.
57 | func mapThrows(_ transform: (Success) throws -> U) -> Result {
58 | switch self {
59 | case .success(let val): return Result { try transform(val) }
60 | case .failure(let e): return .failure(e)
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Tests/CwlViewsCatalog_iOS/Sources/ControlView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ControlView.swift
3 | // CwlViewsCatalog_iOS
4 | //
5 | // Created by Matt Gallagher on 10/2/19.
6 | // Copyright © 2019 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | import CwlViews
21 |
22 | struct ControlViewState: CodableContainer {
23 | let lastEvent: Var
24 | init() {
25 | lastEvent = Var(.noEvent)
26 | }
27 | }
28 |
29 | func controlView(_ controlViewState: ControlViewState, _ navigationItem: NavigationItem) -> ViewControllerConvertible {
30 | return ViewController(
31 | .navigationItem -- navigationItem,
32 | .view -- View(
33 | .backgroundColor -- .white,
34 | .layout -- .center(
35 | .view(Label(.text <-- controlViewState.lastEvent)),
36 | .space(),
37 | .view(
38 | length: 60,
39 | breadth: 220,
40 | Control(
41 | .backgroundColor -- .orange,
42 | .layer -- Layer(.borderWidth -- 2, .borderColor -- UIColor.brown.cgColor, .cornerRadius -- 8),
43 | .action(.touchDown) --> Input().map { .touchDownEvent }.bind(to: controlViewState.lastEvent),
44 | .action(.touchUpInside) --> Input().map { .touchUpEvent }.bind(to: controlViewState.lastEvent),
45 | .action(.touchDragInside) --> Input().map { .touchDragEvent }.bind(to:controlViewState.lastEvent)
46 | )
47 | )
48 | )
49 | )
50 | )
51 | }
52 |
53 | private extension String {
54 | static let noEvent = NSLocalizedString("No actions emitted", comment: "")
55 | static let touchDownEvent = NSLocalizedString("TouchDown emitted", comment: "")
56 | static let touchUpEvent = NSLocalizedString("TouchUp emitted", comment: "")
57 | static let touchDragEvent = NSLocalizedString("Touch drag emitted", comment: "")
58 | }
59 |
--------------------------------------------------------------------------------
/Sources/CwlViewsTesting/iOS/CwlTapGestureRecognizerTesting_iOS.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CwlTapGestureRecognizer_macOS.swift
3 | // CwlViews
4 | //
5 | // Created by Matt Gallagher on 11/3/16.
6 | // Copyright © 2016 Matt Gallagher ( https://www.cocoawithlove.com ). All rights reserved.
7 | //
8 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
9 | // fee is hereby granted, provided that the above copyright notice and this permission notice
10 | // appear in all copies.
11 | //
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
13 | // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITSƒ, WHETHER IN AN ACTION OF CONTRACT,
16 | // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
17 | // OF THIS SOFTWARE.
18 | //
19 |
20 | #if os(iOS)
21 |
22 | extension BindingParser where Downcast: TapGestureRecognizerBinding {
23 | // You can easily convert the `Binding` cases to `BindingParser` using the following Xcode-style regex:
24 | // Replace: case ([^\(]+)\((.+)\)$
25 | // With: public static var $1: BindingParser<$2, TapGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .$1(let x) = \$0 { return x } else { return nil } }, upcast: { \$0.asTapGestureRecognizerBinding() }) }
26 |
27 | // 0. Static bindings are applied at construction and are subsequently immutable.
28 |
29 | // 1. Value bindings may be applied at construction and may subsequently change.
30 | public static var numberOfTapsRequired: BindingParser, TapGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .numberOfTapsRequired(let x) = $0 { return x } else { return nil } }, upcast: { $0.asTapGestureRecognizerBinding() }) }
31 | public static var numberOfTouchesRequired: BindingParser, TapGestureRecognizer.Binding, Downcast> { return .init(extract: { if case .numberOfTouchesRequired(let x) = $0 { return x } else { return nil } }, upcast: { $0.asTapGestureRecognizerBinding() }) }
32 |
33 | // 2. Signal bindings are performed on the object after construction.
34 |
35 | // 3. Action bindings are triggered by the object after construction.
36 |
37 | // 4. Delegate bindings require synchronous evaluation within the object's context.
38 | }
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------